Reputation: 9805
Trying to abstract some algorithm where I only need Set
operations I write
use std::collections::HashSet;
trait Set<T> {
fn insert(self: &mut Self, item: T);
fn contains(self: &Self, item: T) -> bool;
}
impl<T> Set<T> for HashSet<T> {
fn insert(&mut self, item: T) {
self.insert(item);
}
fn contains(&self, item: T) -> bool {
self.contains(item)
}
}
impl<T> Set<T> for Vec<T> {
fn insert(&mut self, item: T) {
self.push(item);
}
fn contains(&self, item: T) -> bool {
self.contains(item)
}
}
I get warning about recursion at compile time, while I "obviously" don't want to recurse but use the underlying implementation on the implementer.
function cannot return without recursing
cannot return without recursing
note: `#[warn(unconditional_recursion)]` on by default
help: a `loop` may express intention better if this is on purposerustc(unconditional_recursion)
I would have expected recursion to happen only if specified (self as Set<T>).insert
What is the idiomatic way to go around that ?
Upvotes: 2
Views: 788
Reputation: 42678
Use specialized calls:
impl<T> Set<T> for HashSet<T> where T: std::cmp::Eq + std::hash::Hash {
fn insert(&mut self, item: T) {
HashSet::insert(self, item);
}
fn contains(&self, item: T) -> bool {
HashSet::contains(self, &item)
}
}
Upvotes: 3
Reputation: 98348
You are right that HashSet::insert
should have priority over your trait function.
But the thing is that HashSet::insert
actually requires T
to implement Eq + Hash
. Since your T
is unrestricted the HashSet::insert
is not considered, the only insert
available is that of the Set
trait, and you get an unexpected recursion.
To discover this you can try compiling:
fn insert(&mut self, item: T) {
HashSet::insert(self, item);
}
and get the error:
error[E0277]: the trait bound `T: std::cmp::Eq` is not satisfied
Fortunately, the Rustc people have considered this posibility of error (it happened to me, too) and have added a very helpful warning.
If you change that to:
impl<T: std::cmp::Eq + std::hash::Hash> Set<T> for HashSet<T> {
fn insert(&mut self, item: T) {
self.insert(item);
}
}
then it works. (There are other unrelated errors around, though.)
Upvotes: 5
Reputation: 1581
You could build a wrapper type to go around that:
use std::collections::HashSet;
trait Set<T> {
fn insert(self: &mut Self, item: T);
fn contains(self: &Self, item: T) -> bool;
}
struct HashSetWrapper<T> {
hash_set: HashSet<T>
}
impl<T> Set<T> for HashSetWrapper<T>
where T: Eq + std::hash::Hash
{
fn insert(&mut self, item: T) {
self.hash_set.insert(item);
}
fn contains(&self, item: T) -> bool {
self.hash_set.contains(&item)
}
}
Note that T has to implement Eq + Hash
Upvotes: 0