我们说我有一些特质:
trait MyTrait {
fn function1(&self);
}
和一些实现它的类型:
struct MyStruct {
number: i32,
}
impl MyTrait for MyStruct {
fn function1(&self) {
printn!("{}", self.number);
}
}
现在我有另一种类型,它希望采用实现MyTrait
的方法。它并不关心他们是否拥有。通过阅读,听起来正确的方法是让它Borrow<X>
而不是X
或&X
或其他。这样就可以采用X
,Rc<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
?
答案 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> {
这要求Consumer
有PhantomData
成员引用其他未使用的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();
}