简体   繁体   English

实施借阅 <Trait> 对于实现Trait的类型

[英]Implementing Borrow<Trait> for a type that implements Trait

Let's say I have some trait: 比方说我有一些特点:

trait MyTrait {
    fn function1(&self);
}

and some type that implements it: 和一些实现它的类型:

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

Now I have another type, which wants to take things that implement MyTrait . 现在我有另一种类型,它想要采取实现MyTrait东西。 It doesn't care whether they're owned or not. 它不关心他们是否拥有。 From reading around, it sounds like the right way to accomplish this is to have it take Borrow<X> instead of X or &X or whatever. 通过阅读,听起来正确的方法就是让Borrow<X>而不是X&X或其他。 This lets it take things of type X , or Rc<X> or Box<X> , etc... 这可以让它采用X ,或Rc<X>Box<X>等类型的东西......

I've got this working when X is a concrete type, but how do I make it work when X is a trait? X是具体类型时,我有这个工作,但是当X是特征时,我该如何使它工作?

Here's what I tried first: 这是我先尝试过的:

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

This doesn't work yet, because MyStruct implements Borrow<MyStruct> , but not Borrow<MyTrait> . 这还不行,因为MyStruct实现Borrow<MyStruct> ,但不实现Borrow<MyTrait> Okay, so let's try to implement that: 好的,让我们试着实现:

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

This gives me the following error, which I don't understand: 这给了我以下错误,我不明白:

 <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 

What? 什么? There aren't any concrete lifetimes mentioned at all in there, and Borrow is defined without any lifetimes mentioned either. 在那里根本没有提到任何具体的生命周期,并且Borrow的定义也没有提及任何生命周期。 I'm stumped. 我很难过。

Firstly, are my assumptions correct that using Borrow is the right way to go? 首先,我的假设是否正确,使用Borrow是正确的方法? And if so, how do I implement Borrow of a trait? 如果是这样,我如何实现Borrow of a trait?

The correct way to write the implementation is this: 编写实现的正确方法是:

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

Trait objects can be restricted with a lifetime bound. 可以使用生命周期限制来限制特征对象。 That's because a type that implements a trait might contain references, and in some situations, we need to be able to differentiate an object that depends on borrowed objects from an object that doesn't. 这是因为实现特征的类型可能包含引用,在某些情况下,我们需要能够区分依赖于借来的对象的对象和不支持对象的对象。 If the lifetime bound is not specified, I think it defaults to 'static ; 如果没有指定生命周期绑定,我认为它默认为'static ; however, specifying &(MyTrait + 'static) as the return type compiles (it's less generic, so you should favor the generic solution above), so the issue you encountered is more subtle than that... 但是,指定&(MyTrait + 'static)作为返回类型编译(它不太通用,所以你应该支持上面的通用解决方案),所以你遇到的问题比那更微妙...

As an aside, I found an alternative way to do this, that doesn't require implementing Borrow<MyTrait> at all: 顺便说一句,我找到了另一种方法,它不需要实现Borrow<MyTrait>

Instead of having impl<T: Borrow<MyTrait> Consumer<T> , we can make Consumer take an additional parameter that designates what the actual borrowed type will be, and then constrain that type to implement the trait. 我们可以让Consumer获取一个额外的参数来指定实际借用的类型,然后约束该类型来实现特征,而不是使用impl<T: Borrow<MyTrait> Consumer<T> Like this: 像这样:

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

This requires Consumer to have a PhantomData member that references the otherwise unused Borrowed type parameter. 这要求Consumer有一个PhantomData成员引用其他未使用的Borrowed类型参数。 Here's a full implementation: 这是一个完整的实现:

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

This alternative has the nice property that it allows using traits with generic methods inside them, because it doesn't require creating any trait objects (trait objects can't be made for traits that have generic functions: see https://doc.rust-lang.org/error-index.html#method-has-generic-type-parameters ). 这个替代方案具有很好的属性,它允许在其中使用具有泛型方法的traits,因为它不需要创建任何trait对象(不能为具有泛型函数的trait创建trait对象:请参阅https://doc.rust -lang.org/error-index.html#method-has-generic-type-parameters )。

The one downside is that Consumer now has to be given hints as to its generic parameters. 一个缺点是Consumer现在必须给出关于其通用参数的提示。 You have to tell it the concrete type involved: 你必须告诉它涉及的具体类型:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM