为Option<T>和Option<&T>实现一个特征

4
假设有一个特质,其所有方法仅接受自身的引用self,例如:
trait Trait {
    fn foo(&self) -> i32;
}

我希望这个特性能够同时适用于Option<T>Option<&T>(因为我并不总是需要所有权),并且可以使用一种简单的实现方式,例如:

impl<T: Trait> Trait for Option<T> {
    fn foo(&self) -> i32 {
        if let Some(inner) = self { return inner.foo(); }
        0
    }
}

impl<T: Trait> Trait for Option<&T> {
    fn foo(&self) -> i32 {
        if let Some(inner) = self { return inner.foo(); }
        0
    }
}

然而,这样做会产生以下错误:

error[E0119]: conflicting implementations of trait `Trait` for type `std::option::Option<&_>`:
  --> option.rs:12:1
   |
5  | impl<T: Trait> Trait for Option<T> {
   | ---------------------------------- first implementation here
...
12 | impl<T: Trait> Trait for Option<&T> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `std::option::Option<&_>`
   |
   = note: downstream crates may implement trait `Trait` for type `&_`

此外,实现方式完全相同。有没有更加简洁的方法来完成这个任务?

这个回答解决了你的问题吗?为引用类型和非引用类型实现trait会导致冲突的实现 - sshashank124
不是这样的。我不想为所有引用类型拥有一个实现,而为所有非引用类型拥有另一个实现。我想要为 Option<T> 实现这个特质,其中 T 是任何可以应用 .foo() 方法的类型,即如果 barT 的一个实例,则调用 bar.foo() 是有效的。 - cripplejayb
作为一种解决方法,您可以仅为Option<&T>实现您的trait,并记录这样一个事实:要想将其与Option<T>一起使用,用户必须首先调用as_ref。严格来说,这不是解决方案,因此我不会将其发布为答案,但它可能很有用。 - Cerberus
1
我已经考虑过了,但是如果这样做,我就无法在包含 Option<T> 字段的结构体上使用 derive 宏来自动派生特性。 - cripplejayb
1个回答

8

这段代码无法编译,因为作为trait使用者的我可能会这样做:

struct Yo;

impl Trait for Yo {
    fn foo(&self) -> i32 { 0 }
}
impl Trait for &Yo {
    fn foo(&self) -> i32 { 1 }
}

fn main() {
    let a = Yo;
    let b: Option<&Yo> = Some(&a);
    b.foo(); // ambiguous call!
}

你的Trait在Option<&Yo>中有两个不同的实现,这会导致冲突!不幸的是,Trait特化仍然不稳定,所以这可能不是一个选择。

如果你愿意的话,你可以通过以下通用的impl解决你的问题:

impl<T: Trait> Trait for &T {
    fn foo(&self) -> i32 {
        (*self).foo()
    }
}

这个与你的 Option<T> 的通用实现相结合,将会为 Option<&T> 提供一个明确的实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接