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.
Generics have two major advantages over trait objects: Speed. When the compiler generates machine code for a generic function, it knows which types it's working with, so it knows at that time which writemethod to call. No need for dynamic dispatch.
A trait is a feature that any given type may or may not support. Think of a trait as a type capability. Rule: For trait methods to be accessible, the trait itself must be in scope! Otherwise, all of its methods are hidden. #![allow(unused)] fn main() { let mut buf: Vec<u8> = vec![]; buf.write_all(b"hello!")?; // ERR: no method named write_all }
Using Traits A trait is a feature that any given type may or may not support. Think of a trait as a type capability. Rule: For trait methods to be accessible, the trait itself must be in scope! Otherwise, all of its methods are hidden. #![allow(unused)] fn main() { let mut buf: Vec<u8> = vec![]; buf.write_all(b"hello!")?;
Intro to Generics A generic function or type can be used with values of many different types.
You have several options.
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);
}
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 {}
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();
}
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) {}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With