我有一个特质
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() {}
4条答案
按热度按时间zrfyljdw1#
这是trait对象的棘手之处,你需要非常明确地知道谁拥有底层对象。
实际上,当你使用trait作为类型时,底层对象必须存储在某个地方,因为trait对象实际上是对实现给定trait的对象的引用。这就是为什么你不能有一个空的
MyTrait
作为类型,它必须是一个引用&MyTrait
或一个框Box<MyTrait>
。带引用
您尝试的第一个方法是使用引用,编译器抱怨缺少生存期说明符:
问题是,一个引用并不拥有底层对象,而另一个对象或作用域必须在某处拥有它:你只是借用了它。因此,编译器需要关于这个引用有效期的信息:如果基础对象被破坏,您的Bar示例将引用释放的内存,这是禁止的!
此处的想法是添加生存期:
你在这里对编译器说的是:“我的Bar对象不能比它内部的Foo引用更有效”。必须指定生存期两次:一次是针对引用的生存期,一次是针对特征对象本身,因为特征可以针对引用实现,如果底层对象是引用,则还必须指定其生存期。
在特殊情况下会书写:
在这种情况下,
'static
要求底层对象必须是一个真实的的结构体,或者是一个&'static
引用,但不允许其他引用。此外,要构建对象,您必须为它提供对您自己存储的其他对象的引用。
你会得到这样的结果:
带框
相反,Box拥有其内容,因此它允许您将底层对象的所有权赋予Bar结构。然而,由于此底层对象可能是一个引用,因此您还需要指定一个生存期:
如果您知道基础对象不能是指涉,也可以写入:
并且寿命问题完全消失。
因此,对象的构造是类似的,但更简单,因为您不需要自己存储底层对象,它由box处理:
在这种情况下,
'static
版本将为:使用裸值
回答您的最后一个问题:
去掉&而只使用自我的含义是什么?
如果一个方法有这样的定义:
这意味着它将在进程中使用您的对象,并且在调用
bar.unwrap()
之后,您将无法再使用bar
。这是一个通常用来给予结构体数据所有权的过程,你会在标准库中遇到很多
unwrap()
函数。amrnrhlw2#
要注意以备将来参考:语法已从
至
按照RFC 438
e5njpo683#
在2021版中,
dyn
现在是Trait对象的一个要求。box
的语法在1.57中仍然不稳定,所以应该使用Box::new()
代替(但是没有必要强制转换它)。所以我认为上面@Levans的例子应该做一点修改:toe950274#
我相信上面的第三个选择是定义一个泛型类型的结构,然后使用特征限定符来实现。这种方法提供了直接的所有权,所以没有生命周期的问题,也没有额外的间接级别。
这就是
BufReader
保存对其inner
Read
示例的引用的方式,例如:来源。