impl トレイト名
は Rust で存在型を扱うのに使える 。しかし、以下はコンパイルが通らない。
use std::fmt::Display; fn f<T>(t: T) -> impl Display where T: Display, { t } fn main() { let mut s = f(""); s = f(s); }
Compiling playground v0.0.1 (/playground) error[E0308]: mismatched types --> src/lib.rs:12:11 | 12 | s = f(s); | ^ expected `&str`, found opaque type | = note: expected reference `&str` found opaque type `impl std::fmt::Display` error: aborting due to previous error For more information about this error, try `rustc --explain E0308`. error: could not compile `playground`. To learn more, run the command again with --verbose.
以下なら通る。
use std::fmt::Display; fn f(t: Box<dyn Display>) -> impl Display { t } fn main() { let mut s = f(Box::new("")); s = f(Box::new(s)); }
これは、 impl トレイト
という型は、関数定義ごとにそれぞれ違う型になるため。例えば、
use std::fmt::Display; fn f(s: &'static str) -> impl Display { s } fn g(s: &'static str) -> impl Display { s } fn main() { let mut s = f("1"); // s = g("2"); // コンパイルできない s = f("3"); }
というコードだと、 f
の戻り値と g
の戻り値は別の型となるので、 f
の戻り値を代入した mutable 変数 s
に g
の戻り値を代入することは許されない。
さて、最初の例に戻ると、 f<T>(t: T) -> impl Display
は型変数 T
を持っている。よって、型変数ごとに関数は別のものとなる。 s
は f::<&str>
の戻り値が代入されているので、 =
で再代入する際も右辺は f::<&str>
でなければ型が合わない。よって、その引数は &str
でなければならないが、 s
の型は ( f::<&str>
の戻り値である) impl Display
であるため、冒頭のエラーメッセージが表示される。
詳しくは、以下の qnighy さんの2年前のエントリの「同一性」の部分に書かれている。