[英]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
,以便返回的值是通過調用consume
對Box
的值獲得的任何值。
我最初寫過
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.