The code below cannot compile due to the type error error[E0283]: type annotations required: cannot resolve _: std::cmp::Eq
. What type annotation is needed in order to compile this code?
This example code is taken from a much larger program where MyHashGenerator::hash_node()
is used to hash nodes in an AST (the type T
relates to a value that is held inside AST nodes, but the definition of the AST is not needed to reproduce the type error).
use std::hash::Hash;
pub trait HashGenerator<T: Clone + Eq + Hash + ToString> {
fn hash(&self, msg: &str) -> u64; // Hash a string.
fn hash_node(&self) -> u64; // Hash an AST node.
}
struct MyHashGenerator {}
impl<T: Clone + Eq + Hash + ToString> HashGenerator<T> for MyHashGenerator {
fn hash(&self, msg: &str) -> u64 {
0
}
fn hash_node(&self) -> u64 {
// error[E0283]: type annotations required: cannot resolve `_: std::cmp::Eq`
self.hash("")
}
}
Code on Playground.
This question is similar to the one here, which has not been answered.
The problem is MyHashGenerator
doesn't take a type argument. So you're making a promise "The same type MyHashGenerator
will suffice as a hash generator and will behave the exact same way regardless of T
". self.hash("")
is a call to hash
on HashGenerator
, but Rust doesn't necessarily know that it's the same HashGenerator
instance that hash_node
was called on. You can make this requirement explicit in one of two ways.
Option 1: Explicit Type Arguments
By explicitly telling Rust what the arguments are, you can avoid this problem.
fn hash_node(&self) -> u64 {
HashGenerator::<T>::hash(self, "")
}
Now it knows to call specifically the <T>
instance, which will suffice.
Option 2: Phantom data
You can parameterize MyHashGenerator
to have an (unused) T
parameter.
use std::marker::PhantomData
...
struct MyHashGenerator<T> {
foo: PhantomData<T>
}
Then Rust can infer which instance you want based on the type of self
, so long as you declare your instance as
impl<T: Clone + Eq + Hash + ToString> HashGenerator<T> for MyHashGenerator<T> {
...
}
Then you don't have to change your implementation of hash_size
.
Personally, I recommend Option 1. It's not as pretty, but it provides the additional API guarantee that things will in fact work the same way regardless of T
. However, if you think MyHashGenerator
might use the T
argument later (or if it does now, and that simply wasn't included in your MCVE), you may consider going with Option 2 to make that dependency more clear.
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