簡體   English   中英

如何從枚舉返回內部類型變體引用而無需到處都有泛型?

[英]How to return an inner type variant reference from an enum without having generics all over the place?

以下是 2 個無法編譯的示例:

type Common<K, V> = HashMap<K, V>;
type Variant1 = Common<u32, u64>;
type Variant2 = Common<i32, i64>;

enum Stuff {
    V1(Variant1),
    V2(Variant2),
}

impl Stuff {
    fn new(variant1: bool) -> Stuff {
        if variant1 {
            Stuff::V1(Variant1::new())
        } else {
            Stuff::V2(Variant2::new())
        }
    }

    // Example 1
    fn get<K, V>(&self) -> &Common<K, V> {
        match self {
            Stuff::V1(x) => x,
            Stuff::V2(x) => x,
        }
    }

    // Example 2
    fn get_key<K, V>(&self, key: K) -> Option<&V> {
        match self {
            Stuff::V1(x) => x.get(key),
            Stuff::V1(x) => x.get(key),
        }
    }
}

操場

error[E0308]: mismatched types
  --> src/main.rs:23:29
   |
21 |     fn get<K, V>(&self) -> &Common<K, V> {
   |            - this type parameter
22 |         match self {
23 |             Stuff::V1(x) => x,
   |                             ^ expected type parameter `K`, found `u32`
   |
   = note: expected reference `&std::collections::HashMap<K, V>`
              found reference `&std::collections::HashMap<u32, u64>`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

error[E0308]: mismatched types
  --> src/main.rs:30:35
   |
28 |     fn get_key<K, V>(&self, key: K) -> &V {
   |                - this type parameter
29 |         match self {
30 |             Stuff::V1(x) => x.get(key),
   |                                   ^^^ expected `&u32`, found type parameter `K`
   |
   = note:   expected reference `&u32`
           found type parameter `K`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

error[E0308]: mismatched types
  --> src/main.rs:30:29
   |
30 |             Stuff::V1(x) => x.get(key),
   |                             ^^^^^^^^^^ expected `&V`, found enum `std::option::Option`
   |
   = note: expected reference `&V`
                   found enum `std::option::Option<&u64>`

error[E0308]: mismatched types
  --> src/main.rs:31:35
   |
28 |     fn get_key<K, V>(&self, key: K) -> &V {
   |                - this type parameter
...
31 |             Stuff::V1(x) => x.get(key),
   |                                   ^^^ expected `&u32`, found type parameter `K`
   |
   = note:   expected reference `&u32`
           found type parameter `K`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

我想要一個替代方案,讓我可以操縱(並從中獲取)變體的內部類型,而無需到處都有泛型。

正如我如何避免將具體結構更改為通用結構所產生的連鎖反應所建議的那樣 ,如果我可以使用帶有結構的Box ,它會起作用:

use std::collections::HashMap;

trait Common<K, V> {
    fn get(&self, key: &K) -> Option<&V>;
}

struct Variant1(HashMap<u32, u64>);
struct Variant2(HashMap<i32, i64>);

impl Common<u32, u64> for Variant1 {
    fn get(&self, key: &u32) -> Option<&u64> {
        self.get(key)
    }
}
impl Common<i32, i64> for Variant2 {
    fn get(&self, key: &i32) -> Option<&i64> {
        self.get(key)
    }
}

struct Stuff<K, V>(Box<dyn Common<K, V>>);

impl<K, V> Stuff<K, V> {
    fn new(variant1: bool) -> Stuff<K, V> {
        if variant1 {
            Stuff(Box::new(Variant1(HashMap::new())))
        } else {
            Stuff(Box::new(Variant2(HashMap::new())))
        }
    }
}

impl<K, V> Common<K, V> for Stuff<K, V> {
    fn get(&self, key: &K) -> Option<&V> {
        self.0.get(key)
    }
}

fn main() {
    let stuff1 = Stuff::new(true);
    let r1 = stuff1.get(&42);
    let stuff2 = Stuff::new(true);
    let r2 = stuff2.get(&42);
}

操場

然而,因為這不再是一個枚舉,我不能再在一個枚舉/結構下創建變體(上面的代碼不能編譯)。

一方面,我希望能夠創建一個包含多個復雜類型(枚舉)的單個結構/枚舉,但另一方面我希望能夠獲取/訪問底層對象。 我找不到同時做這兩件事的方法。

HashMap<u32, u64> != HashMap<i32, i64>

我知道鍵和值類型的大小相同,因此內存中的表示形式將是相似的,但是 rust 不會讓您在不使用unsafe 的情況下在這兩種類型之間進行轉換。

下面的例子沒有使用 unsafe。

您應該注意,此線程中的示例在邏輯上不合理,因為在有符號和無符號整數之間進行轉換會產生不正確的結果。

use std::collections::HashMap;                       

struct Stuff {                                       
    map: HashMap<[u8; 4], [u8; 8]>,                  
}                                                    

impl Stuff {                                         
    fn new() -> Stuff {                              
        Stuff {                                      
            map: HashMap::new(),                     
        }                                            
    }                                                

    fn get_u32_u64(&self, key: u32) -> Option<u64> { 
        self.map                                     
            .get(&key.to_ne_bytes())                 
            .cloned()                                
            .map(u64::from_ne_bytes)                 
    }                                                

    fn get_i32_u64(&self, key: i32) -> Option<u64> { 
        self.map                                     
            .get(&key.to_ne_bytes())                 
            .cloned()                                
            .map(u64::from_ne_bytes)                 
    }                                                

    fn get_u32_i64(&self, key: u32) -> Option<i64> { 
        self.map                                     
            .get(&key.to_ne_bytes())                 
            .cloned()                                
            .map(i64::from_ne_bytes)                 
    }                                                

    fn get_i32_i64(&self, key: i32) -> Option<i64> { 
        self.map                                     
            .get(&key.to_ne_bytes())                 
            .cloned()                                
            .map(i64::from_ne_bytes)                 
    }                                                
}                                                    

這是使用特征的另一種選擇。

use std::collections::HashMap;                                      

struct Stuff {                                                      
    map: HashMap<[u8; 4], [u8; 8]>,                                 
}                                                                   

trait StuffKey {                                                    
    fn key(self) -> [u8; 4];                                        
}                                                                   

trait StuffValue {                                                  
    fn val(val: [u8; 8]) -> Self;                                   
}                                                                   

impl Stuff {                                                        
    fn new() -> Stuff {                                             
        Stuff {                                                     
            map: HashMap::new(),                                    
        }                                                           
    }                                                               

    fn get<K: StuffKey, V: StuffValue>(&self, key: K) -> Option<V> {
        self.map.get(&key.key()).cloned().map(StuffValue::val)      
    }                                                               
}                                                                   

impl StuffKey for i32 {                                             
    fn key(self) -> [u8; 4] {                                       
        self.to_ne_bytes()                                          
    }                                                               
}                                                                   

impl StuffKey for u32 {                                             
    fn key(self) -> [u8; 4] {                                       
        self.to_ne_bytes()                                          
    }                                                               
}                                                                   

impl StuffValue for i64 {                                           
    fn val(val: [u8; 8]) -> Self {                                  
        Self::from_ne_bytes(val)                                    
    }                                                               
}                                                                   

impl StuffValue for u64 {                                           
    fn val(val: [u8; 8]) -> Self {                                  
        Self::from_ne_bytes(val)                                    
    }                                                               
}                                                                   

暫無
暫無

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

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