簡體   English   中英

如何在 Rust 中按值傳遞盒裝特征 object?

[英]How to pass a boxed trait object by value in Rust?

我正在編寫一些代碼,並且有一個特點,即一種按價值獲取self的方法。 我想在Box的特征 object 上調用此方法(使用Box及其值)。 這可能嗎? 如果是這樣,如何?

在代碼方面,最小示例類似於以下(不完整)代碼:

trait Consumable {
    fn consume(self) -> u64;
}
fn consume_box(ptr: Box<dyn Consumable>) -> u64 {
    //what can I put here?
}

我的問題是如何使用指定的簽名填充consume_box ,以便返回的值是通過調用consumeBox的值獲得的任何值。

我最初寫過

ptr.consume()

作為 function 的主體,雖然我意識到這不是完全正確的想法,因為它沒有理解我希望Box被消耗的事實,而不僅僅是它的內容,但這是我唯一能想到的的。 這不會編譯,報錯:

無法移動類型為 dyn Consumable 的值:無法靜態確定 dyn Consumable 的大小

這讓我有些驚訝,作為 Rust 的新手,我曾認為可能self參數的傳遞類似於 C++ 中的右值引用(這正是我想要的 - 在 C++ 中,我可能會通過一個方法來實現它signature virtual std::uint64_t consume() && ,讓std::unique_ptr通過虛擬析構函數清理移出的 object ),但我猜 Rust 確實是按值傳遞,將參數移動到位之前 - 所以這是合理的它拒絕代碼。

問題是,我不確定如何獲得我想要的行為,在那里我可以使用Box的特征 object。我嘗試使用默認實現向特征添加一個方法,認為這可能會讓我在 vtable 中得到一些有用的東西:

trait Consumable {
    fn consume(self) -> u64;
    fn consume_box(me: Box<Self>) -> u64 {
        me.consume()
    }
}

但是,這會產生錯誤

特性Consumable無法制成 object

當我提到Box<dyn Consumable>類型時——這並不奇怪,因為編譯器弄清楚如何處理參數類型隨Self變化的 function 本來就是奇跡。

是否可以使用提供的簽名實現consume_box - 甚至在必要時修改特征?


如果它有用,更具體地說,這是某些數學表達式的一種表示形式的一部分——也許玩具 model 是具體的實現,大致如下所示:

impl Consumable for u64 {
    fn consume(self) -> u64 {
        self
    }
}
struct Sum<A, B>(A, B);
impl<A: Consumable, B: Consumable> Consumable for Sum<A, B> {
    fn consume(self) -> u64 {
        self.0.consume() + self.1.consume()
    }
}
struct Product<A, B>(A, B);
impl<A: Consumable, B: Consumable> Consumable for Product<A, B> {
    fn consume(self) -> u64 {
        self.0.consume() * self.1.consume()
    }
}
fn parse(&str) -> Option<Box<dyn Consumable> > {
    //do fancy stuff
}

其中,在大多數情況下,事物是普通的舊數據(但由於泛型,它可能是任意大的塊),但也讓它與傳遞更多不透明的句柄到這些類型的事物兼容 - 因此希望能夠與Box<dyn Consumable>一起工作。 至少在語言層面,這是一個很好的 model 我在做什么——這些對象擁有的唯一資源是 memory 的片段(與多線程無關,也沒有自引用的惡作劇)——盡管這model 沒有捕捉到我所擁有的用例是一個對實現使用 object 而不是僅僅讀取它有用的用例,它也不恰當 model 我想要一個“開放的” class 可能的段而不是有限集可能性(使得很難像直接表示樹的enum那樣做) - 因此我為什么要問按值傳遞而不是試圖重寫它以按引用傳遞。

目前不支持此功能。 一個dyn Consumable表示一個大小不一的類型,除了通過間接方式(通過引用或類似Box的結構)外,它非常有限。

但是有RFC 1909: Unsized RValues希望放寬這些限制。 一個能夠傳遞未調整大小的 function 參數,例如本例中的self 此 RFC 的當前實現在使用unsized_fn_params每晚編譯時接受您的初始代碼:

#![feature(unsized_fn_params)]

trait Consumable {
    fn consume(self) -> u64;
}

struct Foo;
impl Consumable for Foo {
    fn consume(self) -> u64 {
        42
    } 
}

fn main () {
    let ptr: Box<dyn Consumable> = Box::new(Foo);
    println!("result is {}", ptr.consume());
}

操場上見。

我相信

trait Consumable {
    fn consume(self) -> u64;
}

fn consume_box(val: impl Consumable) -> u64 {
    val.consume()
}

可能會做你想做的事。 I'm all but a Rust expert - or C++ expert for that matter -, but I think it should be working pretty much like the move-semantics in C++ you mentioned in terms of memory behavior. 據我了解,這是一種通用形式,其中 Rust 為您調用的每種類型實現 Function 。

如果不用nightly Rust,我這里寫了個宏。 它自動生成第二個特征 function。

trait Consumable {
    fn consume(self) -> u64;
    fn consume_box(me: Box<Self>) -> u64 ;
}

暫無
暫無

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

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