nicolas
nicolas

Reputation: 9805

Trait implementation calling a function of similar name

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

Answers (3)

Netwave
Netwave

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)
    }
}

Playground

Upvotes: 3

rodrigo
rodrigo

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

Yannick Funk
Yannick Funk

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

Related Questions