To quote the Book (emphasis mine),
The same is true of generic type parameters that are filled in with concrete type parameters when the trait is used: the concrete types become part of the type that implements the trait. When the type is forgotten through the use of a trait object, there is no way to know what types to fill in the generic type parameters with.
I cannot understand the rationale. For a concrete example, consider the following
pub trait Echoer {
fn echo<T>(&self, v: T) -> T;
}
pub struct Foo { }
impl Echoer for Foo {
fn echo<T>(&self, v: T) -> T {
println!("v = {}", v);
return v;
}
}
pub fn passthrough<T>(e: Box<dyn Echoer>, v: T) {
return e.echo(v);
}
fn main() {
let foo = Foo { };
passthrough(foo, 42);
}
The result is, of course, an error
$ cargo run
Compiling gui v0.1.0 (/tmp/gui)
error[E0038]: the trait `Echoer` cannot be made into an object
--> src/main.rs:14:27
|
14 | pub fn passthrough<T>(e: Box<dyn Echoer>, v: T) {
| ^^^^^^^^^^^^^^^ `Echoer` cannot be made into an object
|
= help: consider moving `echo` to another trait
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> src/main.rs:2:8
|
1 | pub trait Echoer {
| ------ this trait cannot be made into an object...
2 | fn echo<T>(&self, v: T) -> T;
| ^^^^ ...because method `echo` has generic type parameters
error: aborting due to previous error
For more information about this error, try `rustc --explain E0038`.
error: could not compile `gui`
To learn more, run the command again with --verbose.
From my understanding, even though e
forgets about its concrete type when being cast into a trait object, it can still infer that it needs to fill the generic type parameter of echo<T>
with i32
, since it's called inside passthrough<T>
, which is monomorphized to passthrough<i32>
at compile time.
What does "the concrete types become part of the type that implements the trait" mean? Why can't trait methods fill their generic type parameters at compile time, e.g. just call echo<i32>
?
echo<i32>
(after compile time type inference)? – Warwickecho
can accept anyT
, so the vtable would have to potentially contain infinitely many possible functions, which isn't possible. Your code may only usei32
, but that doesn't affect the generation of the vtable. – FistulousFoo::echo<i32>
andBar::echo<i32>
. (I'm assuming Rust generates a vtable for each invocation ofpassthrough
, when concrete types are cast into trait objects) – Warwickimpl Echoer for Boo/Bar/Baz
, each vtable contains pointers to all trait methods (even though some are not called throughout the program), and is shared among all invocations of functions with an argument ofBox<dyn Echoer>
? – Warwick