nazo6 notememo

Rust

作成:2021/12/27

更新:2023/10/20

(2021-12-27)

気づきとかいろいろ

エラーハンドリング (2021-12-27)

Result<T, E>Option<T>がある (2021-12-27)

  • okok_or(T)で相互変換可能
  • また下のanyhowで提供されているanyhow::contextを使うことでOptionをResultにできるみたい

汎用的なエラーはResult<T, Box<dyn std::error::Error>> (2021-12-27)

  • Boxはヒープ領域
    • ? 演算子を使ったときにBox::newがいらないのはなぜ?
  • dyn Errorの意味はあとで調べる

と思ったけどanyhowがよさそう?

  • anyhow::Result<T>で上とほぼ同じことができる
  • さらにanyhow::anyhow!マクロで手軽にエラーメッセージを作れる。これは便利

? 演算子 (2021-12-27)

  • ResultOptionを返す関数の中で使うとNoneErrのときに早期リターンできる
  • 関数をreturnするのでブロックの値を返すためには使えない(tauriのcommandsはserdeでエンコードできるものでないといけないから恐らく上のanyhowは使えない?)
    例: これはaにResultが入るわけではない
let a = {
hoge()?
}
  • なので即時関数を使う
    参照:

Rustのクロージャを嗜む - Qiita

はじめにこの記事では、Rustのクロージャに関する奇抜な(JavaScriptのパラダイムに似た)応用を紹介していきます。Rustらしくない書き方だと思うので、こんなこともできるんだ程度に思って…

qiita.com

let a: Result<String, Box<dyn Error>> = ||{
hoge()?
}

非同期 (2021-12-27)

(2021-12-27)

ライブラリ (2021-12-27)

  • async構文自体は言語仕様だがそれを実装するライブラリが必要?よくわかってない
  • async-stdというものもあったがやっぱりデファクトスタンダードはtokioだと思われる

クロージャ (2021-12-27)

  • async || {}という書きかたはunstableらしい。
    • なので|| async {}と書く
    • 即時関数なら(|| async{})().await

reqwest (2021-12-27)

  • 高レベルなHTTPクライアント
ファイルに保存する方法
use futures_util::StreamExt;
use tokio::io::AsyncWriteExt;

let file_dir = path.parent().ok_or(anyhow!("Invalid path"))?;
if !file_dir.is_dir() {
create_dir_all(file_dir).await?;
}
let mut file = tokio::fs::File::create(&path).await?;
while let Some(item) = stream.next().await {
file.write_all_buf(&mut item?).await?;
}
let path_str = path.to_str().ok_or(anyhow!("?"))?;
Ok(path_str.to_string())
  • streamがよくわかってないけどとりあえずこれでいける
    • futures_utilはなんだろう
  • 最後のところをOk(path_str)に変えたらなにかエラーがでた
    • おそらくライフタイム関連の何かだと思うが・・・今は解決できないので保留

非同期handler関数を引数として受けとりさらにそれをtokioでspawnさせる (2022-03-20)

under - Rust

docs.rs

このクレートをパク…参考にした
handler.rs
use async_trait::async_trait;
use std::future::Future;
use std::pin::Pin;

use crate::error::Error;

#[async_trait]
pub trait Handler: Send + Sync + 'static {
#[must_use]
async fn apply(self: Pin<&Self>, request: [request]) -> Result<[response], Error>;
}

#[async_trait]
impl<F, Fut> Handler for F
where
F: Fn(String) -> Fut + Sync + Send + 'static,
Fut: Future<Output = Candidates> + Send + 'static,
{
async fn apply(self: Pin<&Self>, request: [request]) -> Result<[response], Error> {
Ok(self(request).await)
}
}
使用側:
main.rs
#[tokio::main]
async fn main() {
let server = Server::new(handler);
let result = server.start().await;
}
async fn handler(request: [request]) -> [response] {
[response]
}

pub struct Server {
handler: Arc<Pin<Box<dyn Handler>>>,
}

impl Server {
pub fn new<H: Handler>(handler: H) -> Self {
Server {
handler
}
}
pub async fn start(self) -> Result<(), Error> {
let listener = TcpListener::bind((self.address, self.port)).await?;
loop {
let (stream, socket) = listener.accept().await?;

let getter = Arc::clone(&self.candidates_getter);
tokio::spawn(async move {
info!("Socket connected: {}:{}", socket.ip(), socket.port());
Self::process(stream, getter).await
});
}
}
async fn process(
stream: TcpStream,
handler: Arc<Pin<Box<dyn Handler>>>,
) -> Result<(), Error> {
let handler_resut = (*handler).as_ref().apply([handler_prameters]).await;
}
}
正直なんでこれで動くかはよくわかってない
Pinに対する知識が足りてない