Reputation: 44715
With the code:
trait Msg {
fn apply_to_state(&self, state: &mut State);
}
trait ApplicableToStateOne: Msg {
fn apply_to_state(&self, state: &mut State) {
match state {
State::StateOne(s) => {
self.apply_to_state_one(s)
}
_ => {
//TODO: return an error
}
}
}
fn apply_to_state_one(&self, state_one: &mut StateOne);
}
#[derive(Debug, Clone)]
pub struct MsgA {
pub field_a: u8,
}
impl Msg for MsgA {}
impl ApplicableToStateOne for MsgA {
fn apply_to_state_one(&self, state_one: &mut StateOne) {
state_one.one_special += 31; // just a mutability test
}
}
// this is a stub for receiving different kinds of messages from the network
fn recv() -> Box<dyn Msg> {
Box::new(MsgA { field_a: 42 })
}
fn main() {
let mut state = State::StateOne(StateOne { common: 0, one_special: 1 });
for _ in 0..100 { // this would be loop, but that makes the playground timeout
let incoming = recv(); // this would block
incoming.apply_to_state(&mut state)
}
}
( playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7c89a2bbc765380fc002864e2be80e55 )
the compiler complains:
error[E0046]: not all trait items implemented, missing: `apply_to_state`
--> src/bin/sandbox6.rs:83:1
|
2 | fn apply_to_state(&self, state: &mut State);
| -------------------------------------------- `apply_to_state` from trait
...
83 | impl Msg for MsgA {}
| ^^^^^^^^^^^^^^^^^ missing `apply_to_state` in implementation
In my (obviously deficient) understanding, I would have expected the trait ApplicableToStateOne
implementation of apply_to_state
to be called.
How can I make this happen?
Update:
More abstractly, this question is about:
This can all be done, rather verbosely, using enums instead of traits, but that adds a hierarchy of enums.
Having a hierarchy of enums is bad because:
Upvotes: 2
Views: 104
Reputation: 23264
You can use a generic to implement Msg
for everything that implements ApplicableToStateOne
:
struct State {}
trait Msg {
fn apply_to_state(&self, state: &mut State);
}
trait ApplicableToStateOne: Msg {
fn apply_to_state_one(&self, state: &mut State) {
todo!();
}
}
impl<T: ApplicableToStateOne> Msg for T {
fn apply_to_state(&self, state: &mut State) {
self.apply_to_state_one (state);
}
}
#[derive(Debug, Clone)]
pub struct MsgA {
pub field_a: u8,
}
impl ApplicableToStateOne for MsgA {}
// No need to implement Msg explicitly for MsgA
Upvotes: 4
Reputation: 42678
Think of each of the methods to be linked to the Trait
instead of the object itself.
Check this simpler example:
trait Foo {
fn foo(&self) -> &'static str;
}
trait FooPrime {
fn foo(&self) -> &'static str;
}
struct Bar {}
impl Foo for Bar {
fn foo(&self) -> &'static str {
"foo"
}
}
impl FooPrime for Bar {
fn foo(&self) -> &'static str {
"foo prime"
}
}
fn main() {
let bar = Bar{};
println!("{} : {}", bar.foo(), bar.foo());
}
When compiling we get the following error:
Compiling playground v0.0.1 (/playground)
error[E0034]: multiple applicable items in scope
--> src/main.rs:25:29
|
25 | println!("{} : {}", bar.foo(), bar.foo());
| ^^^ multiple `foo` found
|
note: candidate #1 is defined in an impl of the trait `Foo` for the type `Bar`
--> src/main.rs:12:5
|
12 | fn foo(&self) -> &'static str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl of the trait `FooPrime` for the type `Bar`
--> src/main.rs:18:3
|
18 | fn foo(&self) -> &'static str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #1
|
25 | println!("{} : {}", Foo::foo(&bar), bar.foo());
| ^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #2
|
25 | println!("{} : {}", FooPrime::foo(&bar), bar.foo());
| ^^^^^^^^^^^^^^^^^^^
error[E0034]: multiple applicable items in scope
--> src/main.rs:25:40
|
25 | println!("{} : {}", bar.foo(), bar.foo());
| ^^^ multiple `foo` found
|
note: candidate #1 is defined in an impl of the trait `Foo` for the type `Bar`
--> src/main.rs:12:5
|
12 | fn foo(&self) -> &'static str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl of the trait `FooPrime` for the type `Bar`
--> src/main.rs:18:3
|
18 | fn foo(&self) -> &'static str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #1
|
25 | println!("{} : {}", bar.foo(), Foo::foo(&bar));
| ^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #2
|
25 | println!("{} : {}", bar.foo(), FooPrime::foo(&bar));
We need to explicitly tell the compiler which method to use:
fn main() {
let bar = Bar{};
println!("{} : {}", Foo::foo(&bar), FooPrime::foo(&bar));
}
For the same reason in your code the method for the other trait is not used, since it is not part of the previous trait. Here is a link to the book
You may want to use the supertrait functionality for creating a default impl of another trait based on the "parent" one:
trait FooPrime : Foo {
fn foo(&self) -> &'static str {
Foo::foo(self)
}
}
Upvotes: 0