気になった文法を次々記載していく

Future

クラスではなく、trait (いわゆるインターフェース) 共通の機能としての実装。非同期を扱う

await で待つことができるが、できればfuture が ready になるまでメソッド実行したい

impl loop (for () : exec funtion if ready() break) みたいな感じで、ready になるまで fn 実行するループを実装

使えるメソッド https://docs.rs/futures/0.3.21/futures/future/index.html

これの Functions を見る

Future

pub trait Future {
    type Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

Pin と 変更可能な Context を渡して、 Poll 型を返す。返すのは自身っぽい

trait : 関連型 Output : https://doc.rust-lang.org/std/process/struct.Output.html プロセスの構造型

Trait

Trait を定義後、Struct 側でその Trait を実装することで、同じ動作を実現する。 だから interface みたいなものと言える

Struct 側で実装する時は impl を使。impl は struct に関数を追加するみたいな

公式みる

https://doc.rust-lang.org/std/future/index.html

  • Future ライブラリ実装は 4 種類
  • Macro (!で使えるやつ)
  • Struct (構造体。基本は直接は使わない認識)
  • Trait (他のクラスがこの Trait を実装する)
  • Functions (Future 構造体の操作とかはこれ使う認識)

Struct : 基本他で使わない。中身も Private 。直接扱うことはない。他の関数から返却されたりする認識。これ自体は Future 構造体はない

  • PolFn (実験なので、最新の構造体)
  • Pending
  • Ready

Trait : 基本は Future のみ。他のクラスとかでこれを実装する?

type は関連型という概念。いわゆるジェネリック関数作成するために使う感じ? type を記載した場合、ジェネリックで書けるっぽい

pub trait Future {
    type Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

基本のメソッドは一つのみで、 poll のみ。このメソッドは Future を最終的な Object に解決しようとする このメソッドは、値がない時もメインスレッドをブロックしない。 代わりに、現在のタスクは、再度ポーリングすることでさらに進行できるようになったときに起動されるようにスケジュールされています。

poll メソッドに渡されるコンテキストは、現在のタスクを起動するためのハンドルである Waker を提供できます。

基本的には.await を使う

Poll の詳細

この関数は次を返します。 Poll::Pending : 未来がまだ準備されていない場合 Poll::Ready(val) : 正常に終了した場合は、この Futureの結果として val と共に返す。

Pendingの場合、引数として渡した Context から、 Waker のコピーを生成し、それを保存する。(これでスレッドできる?)

await の代わりに poll を使い、返り値を評価し続けその間アニメーションする、といった実装は可能か? Poll::Pending もしくは Context を引数としてアニメーション作成ができそうか。

Struct std::task::Context

impl は 2つ from_waker waker

Module std::task

https://doc.rust-lang.org/std/task/index.html#traits

Wake traits が重要

https://async-book-ja.netlify.app/02_execution/03_wakeups.html この記事のタイマーは重要かも

自作クラス (struct)を作成し、それに Future を実装する

https://async-book-ja.netlify.app/02_execution/03_wakeups.html

impl TimerFuture {
    /// Create a new `TimerFuture` which will complete after the provided
    /// timeout.
    pub fn new(duration: Duration) -> Self {
        let shared_state = Arc::new(Mutex::new(SharedState {
            completed: false,
            waker: None,
        }));

        // Spawn the new thread
        let thread_shared_state = shared_state.clone();
        thread::spawn(move || {
            thread::sleep(duration);
            let mut shared_state = thread_shared_state.lock().unwrap();
            // Signal that the timer has completed and wake up the last
            // task on which the future was polled, if one exists.
            shared_state.completed = true;
            if let Some(waker) = shared_state.waker.take() {
                waker.wake()
            }
        });

        TimerFuture { shared_state }
    }
}

impl で非同期処理を書くことで、外部でそれを使える。 今回はこれで thread::sleep している wake は起動すべきタイミングですべきなので、普通は使わない

Executor

多数のトップレベル future を同時に実行できる、独自のシンプルなエグゼキューターを作成していきます。

検討

  • await の代わりに poll を使い、返り値を評価し続けその間アニメーションする、といった実装は可能か?
  • Poll::Pending もしくは Context を引数としてアニメーション作成ができそうか。
  • Future Trait を上書きする方法は可能か?
  • impl Future で非同期処理でアニメーションを追加するとか?
  • Executor を使う?→違いそう
  • 共通カウンタを引数にとってそれをチェックし続ける

シンプルなやつ

res = hoge

while(res != ready) {
animation
}

理想系

futureobj = hoge
res = anim(futureobj)

or

futureobj = hoge
res = anim(&futureobj)
futureobj.await

Timer でサンプル作る

to_owned()

参照元の値をクローンする。元々参照型な文字列リテラルに使いがち

末尾の?

Result<T,E>型を返す関数内部での処理に付与できる。 その関数がエラーを吐いた場合に、関数が終了しエラーを返す

ok_or(エラー型)

メソッドが返した Option型の値を Result<T,E>型に変換する チェインを作る時に便利

fn hoge() -> Result<(), String> {
  dirs::home_dir()
            .ok_or("error!".to_owned())?
            .to_str()
            .ok_or("error!".to_owned())?
            .to_string()
}

home_dir の結果の Optionを変換し、変換したタイミングで ? が評価され エラー判定される

1つの関数で、複数タイプのエラーが返りそうな場合は、エラーの型を作る必要がある

Result 型

失敗するかもしれない処理の結果を表現する

結果の利用は match 文を使う

例 : get_value_good が Result を返す場合

    match get_value_good(true) {
        Ok(result) => println!("success: {}", result),
        Err(msg) => println!("failure: {}", msg),
    }

#[tokio::main]

main を透過的に非同期関数にするための属性

tokio は Rust で非同期アプリを作成するためのフレームワーク AWS SDK も tokioが必要

冒頭の記載は、マクロ。tokio の非同期処理は Future に基づいているため async ブロック内で使用する必要があり、main を透過的に非同期関数にするための属性