rust 对结构中特征的引用

31moq8wy  于 2022-11-12  发布在  其他
关注(0)|答案(4)|浏览(198)

我有一个特质

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

zrfyljdw1#

这是trait对象的棘手之处,你需要非常明确地知道谁拥有底层对象。
实际上,当你使用trait作为类型时,底层对象必须存储在某个地方,因为trait对象实际上是对实现给定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>
}

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

使用裸值

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

fn unwrap(self) {}

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

amrnrhlw

amrnrhlw2#

要注意以备将来参考:语法已从

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

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

按照RFC 438

e5njpo68

e5njpo683#

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

toe950274#

我相信上面的第三个选择是定义一个泛型类型的结构,然后使用特征限定符来实现。这种方法提供了直接的所有权,所以没有生命周期的问题,也没有额外的间接级别。

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

struct Bar<T> {
    foo: T,
}

impl<T: Foo> Bar<T> {
    fn get_foo(self) -> T {
        let foo = self.foo;
        foo.do_something();
        foo
    }

    fn do_something(&self) -> f64 {
        1.0
    }
}

fn main() {}

这就是BufReader保存对其innerRead示例的引用的方式,例如:来源。

相关问题