rust 我如何指定一个结构域必须实现一个trait?

gpnt7bae  于 2023-01-02  发布在  其他
关注(0)|答案(3)|浏览(211)

我有一个特质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 traitFoowhere a type is expected; tryBoxor&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() {}
fhg3lkii

fhg3lkii1#

这是trait对象的棘手之处,你需要非常明确地说明谁拥有底层对象。
实际上,当你使用trait作为类型时,底层对象必须存储在某个地方,因为trait对象实际上是对实现给定trait的对象的引用,这就是为什么你不能用一个裸的MyTrait作为类型,它必须是一个引用&MyTrait或一个盒子Box<MyTrait>

带参考文献

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

struct Bar {
   foo : &Foo,
}

问题是,一个引用并不拥有底层对象,而另一个对象或作用域必须在某处拥有它:你只是借用它。因此,编译器需要关于这个引用有效期的信息:如果底层对象被破坏,Bar示例将引用释放的内存,这是禁止的!
这里的想法是添加生存期:

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

你在这里对编译器说的是:“我的Bar对象不能比它内部的Foo引用生存”。您必须指定生存期两次:一次是引用的生命周期,一次是trait对象本身,因为trait可以为引用实现,如果底层对象是引用,那么你也必须指定它的生命周期。
在特殊情况下,将写入:

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

带 Package 盒

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

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

如果您知道基础对象不能是引用,则还可以编写:

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

并且寿命问题完全消失。
因此,对象的构造是相似的,但更简单,因为您不需要自己存储底层对象,它由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版本将为:

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

使用裸值

要回答您的最后一个问题:
去掉&而只使用self的含义是什么?
如果一个方法有这样的定义:

fn unwrap(self) {}

这意味着它将在此过程中消耗您的对象,并且在调用bar.unwrap()之后,您将无法再使用bar
这是一个通常用来给予你的struct拥有的数据的所有权的过程,你会在标准库中遇到很多unwrap()函数。

gk7wooem

gk7wooem2#

记下:记下以备将来参考:语法已从

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

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

按照RFC 438

jgzswidk

jgzswidk3#

在2021版中,dyn是Trait对象的一个必要条件,box的语法在1.57版本中仍然不稳定,所以应该使用Box::new()(但是不需要强制转换),所以我认为上面@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));
}

相关问题