5
votes

I have a non-generic struct that implements a generic trait. When I call a function on the struct, I get the following error:

error[E0282]: unable to infer enough type information about `_`
  --> src/main.rs:35:18
   |
35 |     cpu.debugger.attach();
   |                  ^^^^^^ cannot infer type for `_`
   |
   = note: type annotations or generic parameter binding required

I've tried adding type annotations and generic parameter bindings, but I'm obviously doing something wrong; I still can't get it to compile. I have similar code elsewhere with a generic struct that works, presumably because the generic-bounds shared by the struct and trait impl allow the compiler to infer the actual method implementation to call.

The best way to illustrate the issue is with a reduced example:

struct Cpu<M: Memory, D: Debugger<M>> {
    mem: M,
    debugger: D,
}

impl<M: Memory, D: Debugger<M>> Cpu<M, D> {
    fn new(mem: M, debugger: D) -> Self {
        Cpu {
            mem: mem,
            debugger: debugger,
        }
    }
}

trait Memory {}

struct SimpleMemory;

impl Memory for SimpleMemory {}

trait Debugger<M: Memory> {
    fn attach(&mut self) {}
    fn step(mem: &M) {}
}

struct NoOpDebugger;

impl<M: Memory> Debugger<M> for NoOpDebugger {}

fn main() {
    let mut cpu = Cpu::new(SimpleMemory, NoOpDebugger);
    cpu.debugger.attach(); // <-- cannot infer type for `_`
}

Please excuse the poor title, but it's the best way I know how to describe the problem.

1

1 Answers

6
votes

You have several options.

  1. You can specify on which specific trait you want to invoke the attach method.

    fn main() {
        let mut cpu = Cpu::new(SimpleMemory, NoOpDebugger);
        Debugger::<SimpleMemory>::attach(&mut cpu.debugger);
    }
    

    or

    fn main() {
        let mut cpu = Cpu::new(SimpleMemory, NoOpDebugger);
        <NoOpDebugger as Debugger<SimpleMemory>>::attach(&mut cpu.debugger);
    }
    
  2. You can move the attach method to a supertrait that is not generic.

    trait DebuggerBase {
        fn attach(&mut self) {}
    }
    
    trait Debugger<M: Memory>: DebuggerBase {
        fn step(mem: &M) {}
    }
    
    impl DebuggerBase for NoOpDebugger {}
    impl<M: Memory> Debugger<M> for NoOpDebugger {}
    
  3. You can add a PhantomData member to NoOpDebugger and make NoOpDebugger itself generic, so that each NoOpDebugger<M> only implements Debugger<M> for the same M. In your example, the M for NoOpDebugger will be inferred from the call to Cpu::new.

    use std::marker::PhantomData;
    
    struct NoOpDebugger<M>(PhantomData<M>);
    
    impl<M: Memory> Debugger<M> for NoOpDebugger<M> {}
    
    fn main() {
        let mut cpu = Cpu::new(SimpleMemory, NoOpDebugger(PhantomData));
        cpu.debugger.attach();
    }
    
  4. If the implementations of Debugger don't depend on M, and if you don't use Debugger as a trait object, then you can move the type parameter to the methods that need it and omit it on the methods that don't need it.

    trait Debugger {
        fn attach(&mut self) {}
        fn step<M: Memory>(mem: &M) {}
    }