Cheng-Chang Wu
Cheng-Chang Wu

Reputation: 327

Mismatched types error when moving a method from a trait implementation to a trait definition

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

Answers (1)

Paolo Falabella
Paolo Falabella

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

Related Questions