我有一个特征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>,
}
编译 - yay!
但是,当我想要一个函数在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`
将签名更改为
impl Bar {
fn get_foo(&self) -> Box<Foo> {
let this = *self;
this.foo
}
}
但现在我试图取消引用error: cannot move out of dereference of `&`-pointer
{。}}。{/ p>
更改为
self
一切都很好。
...所以
impl Bar {
fn get_foo(self) -> Box<Foo> {
self.foo
}
}
结构中的&
不起作用?我假设我必须装箱
因为结构有一个设置的内存布局所以我们不得不说它是一个指针
特质(因为我们不知道会有多大),但为什么会这样
编译器建议不会编译的东西?bar
中取消引用self
- 我见过的所有示例都使用借用的get_foo()
语法?self
并仅使用&
的含义是什么?学习Rust非常有吸引力,但记忆安全既令人着迷又令人生畏!
编译完整的代码:
self
答案 0 :(得分:95)
这是特征对象的棘手问题,你需要非常清楚谁拥有底层对象。
实际上,当您使用特征作为类型时,底层对象必须存储在某处,因为特征对象实际上是对实现给定特征的对象的引用。这就是为什么你不能将MyTrait
作为一种类型,它必须是引用&MyTrait
或框Box<MyTrait>
。
您尝试的第一种方法是使用引用,编译器抱怨缺少生命周期说明符:
struct Bar {
foo : &Foo,
}
问题是,引用不拥有底层对象,而其他对象或作用域必须在某处拥有它:您只是借用它。因此,编译器需要有关此引用有效期的信息:如果底层对象被销毁,您的Bar实例将引用已释放的内存,这是禁止的!
这里的想法是增加生命周期:
struct Bar<'a> {
foo : &'a (Foo + 'a),
}
你在这里对编译器说的是:“我的Bar对象不能超过它内部的Foo引用”。您必须指定生命周期两次:一次用于引用的生命周期,一次用于trait对象本身,因为traits可以用于引用,如果底层对象是引用,则还必须指定其生命周期。
在特殊情况下会写:
struct Bar<'a> {
foo : &'a (Foo + 'static),
}
在这种情况下,'static
要求底层对象必须是真正的结构或&'static
引用,但不允许其他引用。
此外,要构建对象,您必须为自己存储的其他对象提供引用。
你最终得到这样的东西:
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);
}
A Box反而拥有其内容,因此它允许您将基础对象的所有权授予您的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();
}
回答你的上一个问题:
是什么意思删除&amp;并且只是使用自己?
如果某个方法的定义如下:
fn unwrap(self) {}
这意味着它将在此过程中使用您的对象,并且在调用bar.unwrap()
后,您将无法再使用bar
。
这是一个通常用于回馈您的struct所拥有的数据的所有权的过程。您将在标准库中遇到许多unwrap()
函数。
答案 1 :(得分:13)
要注意以备参考:语法已从
更改struct Bar<'a> {
foo: &'a Foo + 'a,
}
到
struct Bar<'a> {
foo: &'a (Foo + 'a), // with parens
}