参考结构中的特征

我有一个特质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 } } 

都很好。

所以….

  1. 为什么不在& bar结构中工作? 我假设我必须作为结构有一个设置内存布局框,所以我们不得不说,这是一个指向特质的指针(因为我们不知道会有多大),但为什么编译器会提示一些不会编译?
  2. 为什么我不能在get_foo()解除引用self – 我见过的所有例子都使用借用的self语法?
  3. 删除&只是使用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