我有一个特质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>,
}
汇编-耶!
但是,当我希望函数在bar
上返回foo
时-类似于:
impl Bar {
fn get_foo(&self) -> Foo {
self.foo
}
}
很明显,bar.foo
是Box<Foo>
,所以我得到的是error: reference to trait
Foowhere a type is expected; try
Boxor
&Foo``
将签名更改为
impl Bar {
fn get_foo(&self) -> Box<Foo> {
let this = *self;
this.foo
}
}
但是现在我在尝试解引用self
时得到了error: cannot move out of dereference of
&-pointer
。
变更为
impl Bar {
fn get_foo(self) -> Box<Foo> {
self.foo
}
}
一切都好。
所以...
1.为什么bar
结构体中的&
不起作用?我假设我必须装箱,因为结构体有一个设置的内存布局,所以我们必须说它是一个trait的指针(因为我们不知道它会有多大),但是为什么编译器建议一些不能编译的东西?
1.为什么我不能在get_foo()
中取消引用self
-我看到的所有示例都使用借用的self
语法?
1.删除&
而只使用self
的含义是什么?
学习铁 rust 是迷人的,但记忆安全既迷人又吓人!
编译的完整代码:
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() {}
3条答案
按热度按时间fhg3lkii1#
这是trait对象的棘手之处,你需要非常明确地说明谁拥有底层对象。
实际上,当你使用trait作为类型时,底层对象必须存储在某个地方,因为trait对象实际上是对实现给定trait的对象的引用,这就是为什么你不能用一个裸的
MyTrait
作为类型,它必须是一个引用&MyTrait
或一个盒子Box<MyTrait>
。带参考文献
您尝试的第一个方法是使用引用,编译器抱怨缺少生存期说明符:
问题是,一个引用并不拥有底层对象,而另一个对象或作用域必须在某处拥有它:你只是借用它。因此,编译器需要关于这个引用有效期的信息:如果底层对象被破坏,Bar示例将引用释放的内存,这是禁止的!
这里的想法是添加生存期:
你在这里对编译器说的是:“我的Bar对象不能比它内部的Foo引用生存”。您必须指定生存期两次:一次是引用的生命周期,一次是trait对象本身,因为trait可以为引用实现,如果底层对象是引用,那么你也必须指定它的生命周期。
在特殊情况下,将写入:
在这种情况下,
'static
要求底层对象必须是一个真实的的结构体,或者&'static
引用,但是不允许其他引用。此外,要构建对象,必须为它提供对您自己存储的其他对象的引用。
你最终会得到这样的结果:
带 Package 盒
相反,Box拥有其内容,因此它允许您将底层对象的所有权赋予Bar结构。然而,由于此底层对象可能是一个引用,因此您还需要指定一个生存期:
如果您知道基础对象不能是引用,则还可以编写:
并且寿命问题完全消失。
因此,对象的构造是相似的,但更简单,因为您不需要自己存储底层对象,它由box处理:
在这种情况下,
'static
版本将为:使用裸值
要回答您的最后一个问题:
去掉&而只使用self的含义是什么?
如果一个方法有这样的定义:
这意味着它将在此过程中消耗您的对象,并且在调用
bar.unwrap()
之后,您将无法再使用bar
。这是一个通常用来给予你的struct拥有的数据的所有权的过程,你会在标准库中遇到很多
unwrap()
函数。gk7wooem2#
记下:记下以备将来参考:语法已从
到
按照RFC 438
jgzswidk3#
在2021版中,
dyn
是Trait对象的一个必要条件,box
的语法在1.57版本中仍然不稳定,所以应该使用Box::new()
(但是不需要强制转换),所以我认为上面@Levans的例子应该做一些修改: