Reputation: 1072
Trying to override a trait cast problem, described here. Stuck at implementing the trait function which returns the enum instance with own implementation inside:
//the "trait matcher" enum
enum Side<'a> {
Good(&'a GoodDude),
Bad(&'a BadDude),
}
//very general trait
trait Dude {
fn who_am_i(&self) -> Side;
fn do_useful_stuff(&self);
}
//specific trait #1
trait GoodDude: Dude {
fn who_am_i_inner(&self) -> Side {
Side::Good(&self)
}
fn save_the_world(&self);
}
//specific trait #2
trait BadDude: Dude {
fn who_am_i_inner(&self) -> Side {
Side::Bad(&self)
}
fn do_evil(&self);
}
But for some reason the compilation of this part fails with E0277:
trait GoodDude: Dude {
fn who_am_i_inner(&self) -> Side {
Side::Good(&self) //&self should be &GoodDude, but compiler says it is not...
}
fn save_the_world(&self);
}
And results in:
<anon>:16:20: 16:25 error: the trait `GoodDude` is not implemented for the type `&Self` [E0277]
<anon>:16 Side::Good(&self)
^~~~~
<anon>:16:20: 16:25 help: see the detailed explanation for E0277
<anon>:16:20: 16:25 note: required for the cast to the object type `GoodDude`
Can this be worked out?
Full sample: https://play.rust-lang.org/?gist=8ae2384e401da76c16214c4a642ce8b4&version=stable&backtrace=0
Upvotes: 2
Views: 1538
Reputation: 523164
Firstly, the type of self
in fn who_am_i_inner
is already a reference, so you don't need to &
.
fn who_am_i_inner(&self) -> Side {
Side::Good(self)
}
But then rustc complains...
<anon>:13:20: 13:24 error: the trait `core::marker::Sized` is not implemented for the type `Self` [E0277]
<anon>:13 Side::Good(self)
^~~~
<anon>:13:20: 13:24 help: see the detailed explanation for E0277
<anon>:13:20: 13:24 note: `Self` does not have a constant size known at compile-time
<anon>:13:20: 13:24 note: required for the cast to the object type `GoodDude`
Admittedly the error message is very unclear and E0277 is about something totally different. Let's try the nightly compiler instead, which gives better error messages:
error: the trait bound `Self: std::marker::Sized` is not satisfied [--explain E0277]
--> <anon>:13:20
13 |> Side::Good(self)
|> ^^^^
help: consider adding a `where Self: std::marker::Sized` bound
note: required for the cast to the object type `GoodDude`
OK let's try to add the where Self: Sized
:
fn who_am_i_inner(&self) -> Side where Self: Sized {
Side::Good(self)
}
and now it works.
World saved. Press any key to continue
May the 4th be with you
Pew Pew Pew
Luke I am yr father
The where Self: Sized
is Rust's way to signify that the method cannot be used from trait objects. We say the method ignored from "object-safety", or "cannot be virtual" if you like C++.
The effect is that if all you have got is luke: &GoodDude
, then you cannot call luke.who_am_i_inner()
since *luke
has an unknown size.
The reason we need to make the method not object-safe is due to the cast &Self → &GoodDude
. In Rust a trait object reference like &GoodDude
is a fat pointer, internally it is represented as a 2-tuple (pointer, method_table)
. However, in a trait the self
is a thin-pointer.
We cannot convert a thin-pointer to a fat-pointer, since there is a missing information, the method_table. This can be filled in if we knew the concrete type. That's why we add the where Self: Sized
.
If you want to make who_am_i_inner
object-safe, then you cannot provide a default implementation.
Upvotes: 5