Rustのimpl Traitが使えそうで使えない場所
前提の復習
trait objectについて復習している。各書籍にはどう書いてあったんだっけ。
O'ReillyのプログラミングRust
まず、O'ReillyのプログラミングRustだと、11.1.1 トレイトオブジェクトに書いてある。
勝手に今知りたいポイントだけ書くと、 - trait objectはfat pointer(データへのポインタとvtableへのポインタ) - C++とはvtableの持ち方が違う(C++だと構造体側についている) - あるtrait objectが何を指しているのかは、実行時までわからない(仮想メソッドの呼出エラーをチェックするオーバーヘッドが残る)
なんとなくtrait objectを使うと遅い、とは思っていたが、実は今より書き方が少し古いので、dynとかimplとかは出てこない。
実践Rust入門
こちらの書籍では、「8-3 静的ディスパッチと動的ディスパッチ」で書かれているので確認してみる。 要するに、trait objectは動的で、ジェネリクスだと静的ということかな。
簡潔なQ
でも結局、ここを見るのが一番早い(でも何回も見ている)。特にまとめ。
impl Traitが使えそうだけど…
本題。今回だと、parsingを行う関数をつくりたい場合、Read
を実装さえしていれば、.bytes()
とかで処理をかけるので、具体的な型を関数定義で書く必要はない。シグニチャはこんな感じ。
fn read_version(reader: &mut impl Read)
実装途中で処理の共通化をしようと思って、追加の引数に関数を渡せるようにした。
このf
はmap()
に渡してcollect()
する用の関数です。
fn read_vec<R>(reader: &mut impl Read, f: fn(reader: &mut impl Read) -> R) -> Vec<R>
ところが、これだとエラーが出る。
error[E0562]: `impl Trait` not allowed outside of function and inherent method return types --> src/section.rs:99:59 | 99 | fn read_vec<R>(reader: &mut impl Read, f: fn(reader: &mut impl Read) -> R) -> Vec<R> { |
前提で確認したとおり、impl Trait
が使えるのは以下の2つの場合。
で、今回のエラーが出ているのは「引数として渡す関数内の引数」ということで、使える範囲に該当するのでは、と思って書いたのだが、だめだった。
ただ解決は難しくない。
引数でimpl Trait
が使われた場合、以下のように翻訳される。
// before fn give_42_to_callback(callback: impl Fn(i32))
// after fn give_42_to_callback<F: Fn(i32)>(callback: F)
というわけなので、最初から翻訳後の状態で(impl Trait
を使わずに)書けば問題なくビルドが通るようになる。
// before NG fn read_vec<R>(reader: &mut impl Read, f: fn(reader: &mut impl Read) -> R) -> Vec<R>
// after OK fn read_vec<T: Read, R>(reader: &mut T, f: fn(reader: &mut T) -> R) -> Vec<R>
むしろ、こっちの方が少し短くなってスッキリするしいい感じかも(それがエラーの原因ではないだろうけど)。