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年前のエントリの「同一性」の部分に書かれている。