简体   繁体   English

对结构中特征的引用

[英]References to traits in structs

I have a trait Foo我有一个特质Foo

pub trait Foo {
   fn do_something(&self) -> f64;
}

and a struct which references that trait和一个引用该特征的结构

pub struct Bar {
   foo: Foo,
}

Trying to compile I get试图编译我得到

error: reference to trait `Foo` where a type is expected; try `Box<Foo>` or `&Foo`

Changing the struct to将结构更改为

struct Bar {
   foo: &Foo,
}

Tells me error: missing lifetime specifier告诉我error: missing lifetime specifier

Changing the definition to将定义更改为

struct Bar {
   foo: Box<Foo>,
}

Compiles — yay!编译 - 是的!

However, when I want a function to return foo on bar - something like:但是,当我想要一个函数在bar上返回foo时 - 类似于:

impl Bar {
    fn get_foo(&self) -> Foo {
        self.foo
    }
}

Well obviously bar.foo is a Box<Foo> , so expectedly I get error: reference to trait `Foo` where a type is expected; try `Box<Foo>` or `&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`

Changing the signature to将签名更改为

impl Bar {
    fn get_foo(&self) -> Box<Foo> {
        let this = *self;
        this.foo
    }
}

But now I get error: cannot move out of dereference of `&`-pointer on trying to dereference self .但是现在我收到error: cannot move out of dereference of `&`-pointer在尝试取消引用self error: cannot move out of dereference of `&`-pointererror: cannot move out of dereference of `&`-pointer取消引用。

Changing to更改为

impl Bar {
    fn get_foo(self) -> Box<Foo> {
        self.foo
    }
}

Is all good.一切都很好。

So....所以....

  1. Why doesn't & in the bar struct work?为什么&bar结构中不起作用? I'm assuming I have to box as structs have a set memory layout so we have to say it's a pointer to a trait (as we can't know how big that will be), but why does the compiler suggest something that wont compile?我假设我必须装箱,因为结构有一个固定的内存布局,所以我们不得不说它是一个指向特征的指针(因为我们不知道它有多大),但是为什么编译器会建议一些无法编译的东西?
  2. Why can't I dereference self in get_foo() - All examples I've seen use the borrowed self syntax?为什么我不能在get_foo()取消引用self - 我见过的所有示例都使用借用的self语法?
  3. What's the implication of removing the & and just using self ?删除&并仅使用self的含义是什么?

Learning Rust is fascinating, but the memory safety is both fascinating and intimidating!学习 Rust 令人着迷,但内存安全既令人着迷又令人生畏!

Code in full that compiles:编译的完整代码:

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

This is the tricky point of trait objects, you need to be very explicit about who owns the underlying object. 这是trait对象的棘手问题,你需要非常清楚谁拥有底层对象。

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> . 这就是为什么你不能将MyTrait作为一个类型,它必须是一个引用&MyTrait或一个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 ! 因此,编译器需要有关此引用有效的时间的信息:如果底层对象被销毁,则Bar实例将引用已释放的内存,这是禁止的!

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". 你在这里对编译器说的是:“我的Bar对象不能比它里面的Foo引用更长”。 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. 您必须指定生命周期两次:一次用于引用的生命周期,一次用于trait对象本身,因为traits可以用于引用,如果底层对象是引用,则还必须指定其生命周期。

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. 在这种情况下, 'static要求底层对象必须是真正的struct或&'static引用,但不允许其他引用。

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. Box反而拥有其内容,因此它允许您将基础对象的所有权授予Bar结构。 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 : 在这种情况下, '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();
}

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. 这意味着它将在进程中使用您的对象,并且在调用bar.unwrap() ,您将无法再使用bar

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. 您将在标准库中遇到许多unwrap()函数。

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 根据RFC 438

With the 2021 edition, dyn is now a requirement for the Trait objects.在 2021 版中, dyn现在是 Trait 对象的要求 The box syntax is still unstable as of 1.57, so should use Box::new() instead (but there is no need to cast it). box语法从 1.57 开始仍然不稳定,所以应该使用Box::new()代替(但不需要强制转换)。 So I think the above example by @Levans should be modified a bit:所以我认为@Levans 上面的例子应该稍微修改一下:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM