簡體   English   中英

在異構Rust集合中使用Any trait對象有什么問題?

[英]What are the problems of using Any trait objects in heterogeneous Rust collections?

在動態語言(如Clojure)中,很容易表達不同類型的集合:

{:key1 "foo", :key2 [34 "bar" 4.5], "key3" {:key4 "foobar"}}

在Rust中,實現此類集合的首選方法是使用trait對象或枚舉 使用Any trait對象似乎是最靈活的方法(如果沒有固定數量的已知類型替代方法),因為它允許向下轉換到實際的對象類型:

let mut vector: Vec<Box<Any>> = Vec::new();
vector.push(Box::new("I’m"));
vector.push(Box::new(4 as u32));
console!(log, vector[0].downcast_ref::<&str>());
console!(log, vector[1].downcast_ref::<u32>());

似乎不鼓勵這種方法。 它有什么缺點?

它有什么缺點?

主要缺點是,當您從&Any的集合中訪問值時,唯一可以做的就是將其轉換為特定的已知類型。 如果存在您不知道的類型,則該類型的值是完全不透明的:您實際上不能對它們進行任何操作, 只能計算有多少。 如果您知道具體的類型,則可以向下轉換,但是您需要嘗試每種可能的類型。

這是一個例子:

let a = 1u32;
let b = 2.0f32;
let c = "hello";

let v: Vec<&dyn Any> = vec![&a, &b, &c];

match v[0].downcast_ref::<u32>() {
    Some(x) => println!("u32: {:?}", x),
    None => println!("Not a u32!"),
}

注意,我必須顯式向下轉換為u32 使用這種方法將涉及每個可能的具體類型的邏輯分支,並且如果我忘記了情況,則不會出現編譯器警告。

特性對象的用途更加廣泛,因為您不需要知道具體的類型就可以使用值-只要您僅堅持使用特征方法即可。

例如:

let v: Vec<&dyn Debug> = vec![&a, &b, &c];

println!("u32: {:?}", v[0]); // 1
println!("u32: {:?}", v[1]); // 2.0
println!("u32: {:?}", v[2]); // "hello"

我能夠使用所有值而無需知道它們的具體類型,因為我只使用了它們實現Debug的事實。

這兩種方法都有一個缺點,那就是在同類集合中使用具體類型:所有內容都隱藏在指針后面。 訪問數據始終是間接的,並且數據最終可能會散布在內存中,這使得訪問效率大大降低,編譯器更難以優化。

使用枚舉使集合均勻,如下所示:

enum Item<'a> {
    U32(u32),
    F32(f32),
    Str(&'a str),
}

let v: Vec<Item> = vec![Item::U32(a), Item::F32(b), Item::Str(c)];
match v[0] {
    Item::U32(x) => println!("u32: {:?}", x),
    Item::F32(x) => println!("u32: {:?}", x),
    Item::Str(x) => println!("u32: {:?}", x),
}

在這里,我仍然必須了解所有類型,但是如果我錯過了一個,至少會有一個編譯器警告。 還要注意,枚舉可以擁有其值,因此(在這種情況下,除了&str之外)可以將數據緊密包裝在內存中,從而可以更快地訪問它。

總之,對於異構集合, Any很少是正確的答案,但是特征對象和枚舉都有其自身的取舍。

暫無
暫無

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

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