参考结构中的特征
我有一个特质Foo
pub trait Foo { fn do_something(&self) -> f64; }
和一个引用该特征的结构
pub struct Bar { foo: Foo, }
试图编译我得到
error: reference to trait `Foo` where a type is expected; try `Box<Foo>` or `&Foo`
将结构更改为
struct Bar { foo: &Foo, }
告诉我error: missing lifetime specifier
将定义更改为
struct Bar { foo: Box<Foo>, }
编译 – 耶!
然而,当我想要一个函数返回foo
bar
– 如下所示:
impl Bar { fn get_foo(&self) -> Foo { self.foo } }
很明显, bar.foo
是一个Box<Foo>
,所以预计我会得到error: reference to trait `Foo` where a type is expected; try `Box<Foo>` or `&Foo`
error: reference to trait `Foo` where a type is expected; try `Box<Foo>` or `&Foo`
将签名更改为
impl Bar { fn get_foo(&self) -> Box<Foo> { let this = *self; this.foo } }
但现在我得到error: cannot move out of dereference of `&`-pointer
试图解引用self
。
改成
impl Bar { fn get_foo(self) -> Box<Foo> { self.foo } }
都很好。
所以….
- 为什么不在
&
bar
结构中工作? 我假设我必须作为结构有一个设置内存布局框,所以我们不得不说,这是一个指向特质的指针(因为我们不知道会有多大),但为什么编译器会提示一些不会编译? - 为什么我不能在
get_foo()
解除引用self
– 我见过的所有例子都使用借用的self
语法? - 删除
&
只是使用self
的含义是什么?
学习铁锈是迷人的,但记忆安全既迷人又令人生畏!
编译完整的代码:
trait Foo { fn do_something(&self) -> f64; } struct Bar { foo: Box<Foo>, } impl Bar { fn get_foo(self) -> Box<Foo> { let foo = self.foo; foo.do_something(); foo } } fn main() {}
这是特质对象的难点,你需要非常明确谁拥有底层对象。
事实上,当你使用特质作为一个types时,底层对象必须存储在某个地方,因为特质对象实际上是对实现给定特征的对象的引用。 这就是为什么你不能有一个MyTrait
作为一个types,它必须是一个引用&MyTrait
或一个Box<MyTrait>
。
带有参考
你尝试的第一种方法是用引用,编译器抱怨缺less生命周期说明符:
struct Bar { foo : &Foo, }
问题是,引用不拥有底层对象,而其他对象或范围必须拥有它在某处:你只是借用它。 因此,编译器需要有关这个引用有效的信息:如果底层对象被销毁,你的Bar实例将有一个被释放的内存的引用,这是被禁止的!
这里的想法是添加生命周期:
struct Bar<'a> { foo : &'a (Foo + 'a), }
你在这里给编译器说的是:“我的Bar对象不能在它里面的Foo引用超时”。 您必须指定生命周期两次:一次为引用的生命周期,一次为特征对象本身,因为可以为引用实现特征,并且如果基础对象是引用,则还必须指定其生存期。
特殊情况下会写:
struct Bar<'a> { foo : &'a (Foo + 'static), }
在这种情况下, 'static
要求底层对象必须是一个真实的结构或一个&'static
引用,但其他引用将不被允许。
另外,为了build立你的对象,你必须给它一个你自己存储的其他对象的引用。
你最终会得到这样的结果:
trait Foo {} struct MyFoo; impl Foo for MyFoo {} struct Bar<'a> { foo: &'a (Foo + 'a), } impl<'a> Bar<'a> { fn new(the_foo: &'a Foo) -> Bar<'a> { Bar { foo: the_foo } } fn get_foo(&'a self) -> &'a Foo { self.foo } } fn main() { let myfoo = MyFoo; let mybar = Bar::new(&myfoo as &Foo); }
与箱子
一个框相反拥有它的内容,因此它允许你把基础对象的所有权给你的Bar结构体。 然而,由于这个潜在的对象可能是一个参考,你还需要指定一个生命周期:
struct Bar<'a> { foo: Box<Foo + 'a> }
如果你知道底层对象不能作为参考,你也可以写:
struct Bar { foo: Box<Foo + 'static> }
终生问题完全消失。
对象的构造是相似的,但更简单,因为你不需要自己存储底层对象,它由框来处理:
trait Foo {} struct MyFoo; impl Foo for MyFoo {} struct Bar<'a> { foo: Box<Foo + 'a>, } impl<'a> Bar<'a> { fn new(the_foo: Box<Foo + 'a>) -> Bar<'a> { Bar { foo: the_foo } } fn get_foo(&'a self) -> &'a Foo { &*self.foo } } fn main() { let mybar = Bar::new(box MyFoo as Box<Foo>); }
在这种情况下, 'static
版本将是:
trait Foo {} struct MyFoo; impl Foo for MyFoo {} struct Bar { foo: Box<Foo + 'static>, } impl Bar { fn new(the_foo: Box<Foo + 'static>) -> Bar { Bar { foo: the_foo } } fn get_foo<'a>(&'a self) -> &'a Foo { &*self.foo } } fn main() { let mybar = Bar::new(box MyFoo as Box<Foo>); let x = mybar.get_foo(); }
用裸价值
回答你的最后一个问题:
什么是去除&只是使用自我的含义?
如果一个方法有这样的定义:
fn unwrap(self) {}
这意味着它将消耗过程中的对象,并在调用bar.unwrap()
,将无法再使用bar
。
这是一个通常用来返回你的结构所拥有的数据所有权的过程。 你会在标准库中遇到很多unwrap()
函数。
注意将来的参考:语法已经改变
struct Bar<'a> { foo: &'a Foo + 'a, }
至
struct Bar<'a> { foo: &'a (Foo + 'a), // with parens }
根据RFC 438