6

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 Answer 1

8

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) {}
    }
    
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.