error[E0782]: expected type, found trait 'Xxx'
型が要求される箇所にトレイトを使用した。
トレイトは型を分類し抽象化する。
抽象化には二種類あり、コンパイル時の抽象化を担うのが静的ディスパッチ、実行時の抽象化を担うのが動的ディスパッチである。前者は具体的な型の指定を不要にし、後者は実行時に同種のトレイトへの参照どうしを交換可能にする。
impl と dyn
現在の仕様では、静的ディスパッチ型か動的ディスパッチ型かは、トレイト名に先行する impl と dyn により、明示的な区別が必須である。
一方、Rust 2021 Edition 以前では、両者は暗黙的に区別されるだけで、どちらもトレイト名を書くだけだった。両者は似ているが重要な箇所で異なっており、これは混乱の原因だった。このような経緯があるため、一部のこのエラー番号のエラーは以前は合法だった。
過去の筆者も含め、トレイトを型の一種だと考えている Rust の初心者は多い。しかし、トレイトは型を抽象化するだけで、それ自身は型ではない。そのため、型が要求される箇所にトレイトを直接は指定できない。一方、dyn と impl を指定した動的ディスパッチ型や静的ディスパッチ型は正式に型として扱われる。
ちなみに、この誤解の切っ掛けは他の言語によくある機能、インターフェースだろう。と言うのも、インターフェースはトレイトと同じく型の抽象化が目的で、またそれ自身が型である。そのため、型を要求している箇所にはインターフェースもだいたい指定できる。
このパターンはそもそも間違っているため根本的な見直しが必要である。
以下では、関数 show_default の型引数にトレイト MyTrait を指定してしまっている。
use std::fmt::Display;
fn main() {
show_default::<MyTrait>();
}
fn show_default<T: Default + Display>() {
println!("{}", T::default())
}
trait MyTrait: Default + Display {}
error[E0782]: expected a type, found a trait --> src\main.rs:4:20 | 4 | show_default::<MyTrait>(); | ^^^^^^^ | help: you can add the `dyn` keyword if you want a trait object | 4 | show_default::<dyn MyTrait>(); | +++
このパターンは impl や dyn を指定すると治る。
以下では、変数 x の型の内部で dyn なしのトレイトを指定している。
fn main() {
let x: &ShowValue = &42;
x.show_value();
}
trait ShowValue {
fn show_value(&self);
}
impl ShowValue for i32 {
fn show_value(&self) {
println!("The value is {self}");
}
}
error[E0782]: expected a type, found a trait --> src\main.rs:2:13 | 2 | let x: &ShowValue = &42; | ^^^^^^^^^ | help: you can add the `dyn` keyword if you want a trait object | 2 | let x: &dyn ShowValue = &42; | +++