Reputation: 327
I have two structs, VM
and Word
. I need a new struct Control
which behaves just like VM
, but with one more field master
. Because Rust has no inheritance, I try to extend the struct through composition. I move the functions of VM
into a new trait Core
, and implement Core
for Control
. The resulting code works.
struct Word<T> {
action: fn(target: &T)
}
struct VM {
word: Word<VM>
}
trait Core<T> {
fn word(&self) -> &Word<T>;
fn hello(&self) { println!("Hello"); }
fn execute(&self);
}
impl Core<VM> for VM {
fn word(&self) -> &Word<VM> { &self.word }
fn execute(&self) { (self.word().action)(self); }
}
struct Control {
word: Word<Control>,
master: i32,
}
impl Core<Control> for Control {
fn word(&self) -> &Word<Control> { &self.word }
fn execute(&self) { (self.word().action)(self); }
}
fn main() {
let vm = VM{
word: Word {action: Core::hello}
};
vm.execute();
let control = Control{
word: Word {action: Core::hello},
master: 0,
};
vm.execute();
}
The two implementation of execute
are identical. So I move execute
into trait Core
.
trait Core<T> {
fn word(&self) -> &Word<T>;
fn hello(&self) { println!("Hello"); }
fn execute(&self) { (self.word().action)(self); }
}
impl Core<VM> for VM {
fn word(&self) -> &Word<VM> { &self.word }
}
impl Core<Control> for Control {
fn word(&self) -> &Word<Control> { &self.word }
}
Which compiles with the following error:
main.rs:14:44: 14:48 error: mismatched types:
expected `&T`,
found `&Self`
(expected type parameter,
found Self) [E0308]
main.rs:14 fn execute(&self) { (self.word().action)(self); }
How can I solve this problem?
Upvotes: 2
Views: 125
Reputation: 25844
if you move execute in Core
, there is nothing in the trait definition that says that T
is the same type as Self
.
trait Core<T> {
fn word(&self) -> &Word<T>;
fn hello(&self) { println!("Hello"); }
fn execute(&self) {
(self.word() // this is a &Word<T>
.action) // this is a fn(T)
(self); // this type is Self. T is not necessarily = Self
}
}
When execute
is in the impl Core<Control> for Control
, the impl says that Self
and T
are both = Control
, so execute
works. But if T can be anything like in the definition of the trait, Rust can't let your code compile.
How to fix it depends on what you need to do.
If your trait is always meant to be implemented this way (impl Core<Something> for Something
or impl Core<SomethingElse> for SomethingElse
but NEVER as impl Core<Something> for SomethingElse
), you can drop the parameter from the trait definition and have just:
trait Core: Sized {
fn word(&self) -> &Word<Self>; // now you can't parametrize what
// Word to return. It will be Self.
fn hello(&self) { println!("Hello"); }
fn execute(&self) { (self.word().action)(self); } // ...and this works
}
Upvotes: 2