结构中对 trait 的引用

我有 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.fooBox<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结构中不工作? 我假设我必须打拳击 因为 struct 有一个集合内存布局,所以我们必须说它是一个指针 (因为我们不知道它会有多大) ,但是为什么 编译器建议一些不能编译的东西?
  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() {}
30227 次浏览

This is the tricky point of trait objects, you need to be very explicit about who owns the underlying object.

Indeed, when you use a trait as a type, the underlying object must be stored somewhere, as trait objects are in fact references to an object implementing the given trait. This is why you cannot have a bare MyTrait as a type, it must be either a reference &MyTrait or a box Box<MyTrait>.

With references

The first method you tried was was with a reference and the compiler complained about a missing lifetime specifier :

struct Bar {
foo : &Foo,
}

The problem is, a reference doesn't own the underlying object, and an other object or scope must own it somewhere: you are only borrowing it. And thus, the compiler need information about how long this reference will be valid: if the underlying object was destroyed, your Bar instance would have a reference to freed memory, which is forbidden !

The idea here is to add lifetimes:

struct Bar<'a> {
foo : &'a (Foo + 'a),
}

What you are saying here to the compiler is : "My Bar object cannot outlive the Foo reference inside it". You have to specify the lifetime two times : once for the lifetime of the reference, and once for the trait object itself, because traits can be implemented for references, and if the underlying object is a reference, you must specify its lifetime as well.

On special case would be writing:

struct Bar<'a> {
foo : &'a (Foo + 'static),
}

In this case, the 'static requires that the underlying object must be a real struct, or a &'static reference, but other references won't be allowed.

Also, to build your object, you will have to give it a reference to an other object you store yourself.

You end up with something like this :

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);
}

With Boxes

A Box contrarily owns its content, thus it allows you to give ownership of the underlying object to your Bar struct. Yet, as this underlying object could be a reference, you need to specify a lifetime as well :

struct Bar<'a> {
foo: Box<Foo + 'a>
}

If your know that the underlying object cannot be a reference, you can also write:

struct Bar {
foo: Box<Foo + 'static>
}

and the lifetime problem disappears completely.

The construction of the object is thus similar, but simpler as you don't need to store the underlying object yourself, it is handled by the box :

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>);
}

In this case, the 'static version would be :

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();
}

With the bare value

To answer your last question :

Whats the implication of removing the & and just using self?

If a method has a definition like this :

fn unwrap(self) {}

It means it will consume your object in the process, and after calling bar.unwrap(), you won't be able to use bar any longer.

It is a process used generally to give back ownership of the data your struct owned. You'll meet a lot of unwrap() functions in the standard library.

To note for future reference: the syntax has changed from

struct Bar<'a> {
foo: &'a Foo + 'a,
}

to

struct Bar<'a> {
foo: &'a (Foo + 'a), // with parens
}

Per RFC 438

With the 2021 edition, dyn is now a requirement for the Trait objects. The box syntax is still unstable as of 1.57, so should use Box::new() instead (but there is no need to cast it). So I think the above example by @Levans should be modified a bit:

trait Foo {}


struct MyFoo;


impl Foo for MyFoo {}


struct Bar<'a> {
foo: Box<dyn Foo + 'a>,
}


impl<'a> Bar<'a> {
fn new(the_foo: Box<dyn Foo + 'a>) -> Bar<'a> {
Bar { foo: the_foo }
}


fn get_foo(&'a self) -> &'a dyn Foo {
&*self.foo
}
}


fn main() {
let mybar = Bar::new(Box::new(MyFoo));
}