gpt4 book ai didi

rust - 如何指定结构字段必须实现特征?

转载 作者:行者123 更新时间:2023-11-29 07:40:06 25 4
gpt4 key购买 nike

我有一个特质 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>,
}

编译——耶!

但是,当我想要一个函数返回 foo 时在 bar - 类似的东西:

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结构工作?我假设我必须装箱因为结构有一个固定的内存布局所以我们不得不说它是一个指针到一个特征(因为我们不知道它会有多大),但是为什么编译器建议一些无法编译的东西?
  2. 为什么我不能取消引用 selfget_foo() - 我见过的所有示例都使用借来的 self语法?
  3. 删除 & 的含义是什么?并且只使用 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() {}

最佳答案

这是 trait 对象的棘手点,你需要非常明确地知道谁拥有底层对象。

确实,当您将特征用作类型时,底层对象必须存储在某个地方,因为特征对象实际上是对实现给定特征的对象的引用。这就是为什么你不能有一个裸MyTrait作为一种类型,它必须是引用 &MyTrait或一个盒子Box<MyTrait> .

引用文献

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

struct Bar {
foo : &Foo,
}

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

这里的想法是增加生命周期:

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

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

在特殊情况下会写:

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

在这种情况下,'static要求底层对象必须是一个真正的结构,或者一个 &'static引用,但不允许其他引用。

此外,要构建您的对象,您必须为其提供对您自己存储的其他对象的引用。

你最终得到这样的结果:

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 结构。然而,由于这个底层对象可能是一个引用,您还需要指定一个生命周期:

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

如果你知道底层对象不能是引用,你也可以这样写:

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

生命周期问题完全消失。

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

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版本将是:

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

用裸值

回答你的最后一个问题:

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

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

fn unwrap(self) {}

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

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

关于rust - 如何指定结构字段必须实现特征?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26212397/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com