簡體   English   中英

對結構中特征的引用

[英]References to traits in structs

我有一個特質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.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`

將簽名更改為

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

但是現在我收到error: cannot move out of dereference of `&`-pointer在嘗試取消引用self error: cannot move out of dereference of `&`-pointererror: cannot move out of dereference of `&`-pointer取消引用。

更改為

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

一切都很好。

所以....

  1. 為什么&bar結構中不起作用? 我假設我必須裝箱,因為結構有一個固定的內存布局,所以我們不得不說它是一個指向特征的指針(因為我們不知道它有多大),但是為什么編譯器會建議一些無法編譯的東西?
  2. 為什么我不能在get_foo()取消引用self - 我見過的所有示例都使用借用的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引用更長”。 您必須指定生命周期兩次:一次用於引用的生命周期,一次用於trait對象本身,因為traits可以用於引用,如果底層對象是引用,則還必須指定其生命周期。

在特殊情況下將寫作:

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

在這種情況下, 'static要求底層對象必須是真正的struct或&'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();
}

隨着光值

回答你的上一個問題:

是什么意思去除&只是使用自我?

如果方法有這樣的定義:

fn unwrap(self) {}

這意味着它將在進程中使用您的對象,並且在調用bar.unwrap() ,您將無法再使用bar

這是一個通常用於回饋結構所擁有的數據所有權的過程。 您將在標准庫中遇到許多unwrap()函數。

要注意以備將來參考:語法已更改

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

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

根據RFC 438

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM