对结构中特征的引用 [英] References to traits in structs

查看:28
本文介绍了对结构中特征的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个特质 Foo

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

和一个引用该特征的结构

and a struct which references that trait

pub struct Bar {
   foo: Foo,
}

尝试编译我得到

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

将结构更改为

struct Bar {
   foo: &Foo,
}

告诉我错误:缺少生命周期说明符

将定义更改为

struct Bar {
   foo: Box<Foo>,
}

编译 - 是的!

但是,当我想要一个函数在 bar 上返回 foo - 类似于:

However, when I want a function to return foo on bar - something like:

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

很明显 bar.foo 是一个 Box,所以我会得到 error: reference to trait `Foo` 其中需要一个类型;试试 `Box` 或 `&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`

将签名更改为

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

但现在我收到 error: cannot move out of dereference of `&`-pointer 试图取消引用 self.

But now I get error: cannot move out of dereference of `&`-pointer on trying to dereference self.

更改为

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

一切都好.

所以....

  1. 为什么 bar 结构中的 & 不起作用?我假设我必须装箱因为结构有一个固定的内存布局所以我们不得不说它是一个指针到一个特征(因为我们不知道它会有多大),但是为什么编译器建议一些不会编译的东西?
  2. 为什么我不能在 get_foo() 中取消引用 self - 我见过的所有示例都使用借用的 self 语法?立>
  3. 删除 & 并只使用 self 意味着什么?
  1. Why doesn't & in the bar struct work? 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?
  3. What's the implication of removing the & and just using self?

学习 Rust 令人着迷,但内存安全既令人着迷又令人生畏!

Learning Rust is fascinating, but the memory safety is both fascinating and intimidating!

编译的完整代码:

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

推荐答案

这是 trait 对象的棘手之处,你需要非常明确谁拥有底层对象.

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

确实,当您使用 trait 作为类型时,底层对象必须存储在某处,因为 trait 对象实际上是对实现给定 trait 的对象的引用.这就是为什么你不能将 MyTrait 作为类型,它必须是引用 &MyTrait 或框 Box.

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>.

您尝试的第一种方法是使用引用,编译器抱怨缺少生命周期说明符:

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

struct Bar {
   foo : &Foo,
}

问题是,引用并不拥有底层对象,而其他对象或作用域必须在某处拥有它:您只是借用了它.因此,编译器需要有关此引用有效多长时间的信息:如果底层对象被破坏,您的 Bar 实例将拥有对已释放内存的引用,这是被禁止的!

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

您在这里对编译器说的是:我的 Bar 对象不能比其中的 Foo 引用更长寿".你必须两次指定生命周期:一次是引用的生命周期,一次是特征对象本身,因为特征可以为引用实现,如果底层对象是引用,你也必须指定它的生命周期.

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.

在特殊情况下会写:

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

在这种情况下,'static 要求底层对象必须是一个真正的结构体,或者一个 &'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);
}

带盒子

相反,Box 拥有其内容,因此它允许您将底层对象的所有权授予 Bar 结构.然而,由于这个底层对象可能是一个引用,你还需要指定一个生命周期:

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

寿命问题完全消失.

对象的构造因此类似,但更简单,因为您不需要自己存储底层对象,它由框处理:

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

在这种情况下,'static 版本将是:

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 :

删除 & 的含义是什么?并且只使用 self?

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

如果一个方法有这样的定义:

If a method has a definition like this :

fn unwrap(self) {}

这意味着它会在这个过程中消耗你的对象,在调用bar.unwrap()之后,你将不能再使用bar.

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.

这是一个通常用于返还您的结构所拥有的数据的所有权的过程.您会在标准库中遇到很多 unwrap() 函数.

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.

这篇关于对结构中特征的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆