特征实现调用相似名称的函数

Trait implementation calling a function of similar name

尝试抽象一些我只需要 Set 我编写的操作的算法

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

我在编译时收到关于递归的警告,而我“显然”不想递归而是在实现者上使用底层实现。

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)

我原以为只有在指定的情况下才会发生递归(self as Set<T>).insert

解决这个问题的惯用方法是什么?

您可以构建一个包装器类型来解决这个问题:

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

注意 T 必须实现 Eq + 哈希

你是对的 HashSet::insert 应该优先于你的特征函数。

但问题是 HashSet::insert 实际上需要 T 来实现 Eq + Hash。由于您的 T 不受限制,因此不考虑 HashSet::insert,唯一可用的 insertSet 特征,您会得到意外的递归。

要发现这一点,您可以尝试编译:

    fn insert(&mut self, item: T) {
        HashSet::insert(self, item);
    }

并得到错误:

error[E0277]: the trait bound `T: std::cmp::Eq` is not satisfied

幸运的是,Rustc 的人已经考虑到这种错误的可能性(我也发生过)并添加了一个非常有用的警告。

如果将其更改为:

impl<T: std::cmp::Eq + std::hash::Hash> Set<T> for HashSet<T> {
    fn insert(&mut self, item: T) {
        self.insert(item);
    }
}

然后就可以了。 (不过,还有其他不相关的错误。)

使用专门调用:

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