为实现Trait的类型实现Borrow <trait>

时间:2016-04-07 01:14:30

标签: rust

我们说我有一些特质:

trait MyTrait {
    fn function1(&self);
}

和一些实现它的类型:

struct MyStruct {
    number: i32,
}
impl MyTrait for MyStruct {
    fn function1(&self) {
        printn!("{}", self.number);
    }
}

现在我有另一种类型,它希望采用实现MyTrait的方法。它并不关心他们是否拥有。通过阅读,听起来正确的方法是让它Borrow<X>而不是X&X或其他。这样就可以采用XRc<X>Box<X>等类型的内容......

X是具体类型时,我已经开始工作了,但如果X是特质,我该如何使它工作?

这是我先尝试过的事情:

struct Consumer<T> {
    inner: T
}

impl<T: Borrow<MyTrait>> Consumer<T> {
    pub fn new(inner: T) -> Consumer<T> {
        Consumer {
            inner: inner
        }
    }
    pub fn do_stuff(&self) {
        self.inner.borrow().function1();
    }
}

fn main() {
    // I want to eventually be able to swap this out for x = Rc::new(MyStruct ...
    // but I'll keep it simple for now.
    let x = MyStruct { number: 42 };
    let c = Consumer::new(x);
    c.do_stuff();
}

这还不起作用,因为MyStruct实现了Borrow<MyStruct>,而不是Borrow<MyTrait>。好的,让我们试着实现:

impl Borrow<MyTrait> for MyStruct {
    fn borrow(&self) -> &MyTrait {
        self
    }
}

这给了我以下错误,我不明白:

<anon>:33:5: 35:6 error: method `borrow` has an incompatible type for trait:
 expected bound lifetime parameter ,
    found concrete lifetime [E0053]
<anon>:33     fn borrow(&self) -> &MyTrait {
<anon>:34         self
<anon>:35     }
<anon>:33:5: 35:6 help: see the detailed explanation for E0053
error: aborting due to previous error
playpen: application terminated with error code 101

什么?在那里根本没有提到任何具体的生命周期,Borrow的定义也没有提到任何生命周期。我很难过。

首先,我的假设是否正确,使用Borrow是正确的方法?如果是这样,我该如何实现特征的Borrow

2 个答案:

答案 0 :(得分:7)

编写实现的正确方法是:

impl<'a> Borrow<MyTrait + 'a> for MyStruct {
    fn borrow(&self) -> &(MyTrait + 'a) {
        self
    }
}

可以使用生命周期限制来限制特征对象。这是因为实现特征的类型可能包含引用,在某些情况下,我们需要能够区分依赖于借来的对象的对象和不具有该对象的对象。如果未指定生命周期绑定,我认为它默认为'static;但是,指定&(MyTrait + 'static)作为返回类型编译(它不那么通用,所以你应该支持上面的通用解决方案),所以你遇到的问题比那个更微妙......

答案 1 :(得分:4)

顺便说一句,我找到了另一种方法来做到这一点,根本不需要实施Borrow<MyTrait>

我们可以让impl<T: Borrow<MyTrait> Consumer<T>取一个指定实际借用类型的附加参数,然后约束该类型来实现特征,而不是Consumer。像这样:

impl<T: Borrow<Borrowed>, Borrowed: MyTrait> Consumer<T, Borrowed> {

这要求ConsumerPhantomData成员引用其他未使用的Borrowed类型参数。这是一个完整的实现:

struct Consumer<T, Borrowed> {
    inner: T,
    phantom: PhantomData<Borrowed>
}

impl<T: Borrow<Borrowed>, Borrowed: MyTrait> Consumer<T, Borrowed> {
    fn new(inner: T) -> Consumer<T, Borrowed> {
        Consumer {
            inner: inner,
            phantom: PhantomData
        }
    }
    pub fn do_stuff(&self) { // this function is the same as before.
        self.inner.borrow().function1();
    }
}

这个替代方案具有很好的属性,它允许在其中使用具有泛型方法的特征,因为它不需要创建任何特征对象(特征对象不能用于具有泛型函数的特征:请参阅https://doc.rust-lang.org/error-index.html#method-has-generic-type-parameters)。

一个缺点是Consumer现在必须给出关于其通用参数的提示。你必须告诉它所涉及的具体类型:

fn main() {
    let x = MyStruct { number: 42 };
    let c = Consumer::<_, MyStruct>::new(x);
    c.do_stuff();
}