rust - 如何从枚举返回内部类型变量引用而又没有遍历泛型?

标签 rust

以下是两个未编译的示例:

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),
        }
    }
}

playground

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

我想有一个替代方法,使我可以在不使用泛型的情况下操纵变量的内部类型(并从中获取信息)。

How can I avoid a ripple effect from changing a concrete struct to generic?中的建议,如果我可以将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);
}

playground

但是,因为它不再是枚举,所以我不能再在一个枚举/结构下创建变体(上面的代码无法编译)。

一方面,我希望能够创建一个包含多个复杂类型(枚举)的结构/枚举,但是另一方面,我希望能够获取/访问基础对象。我找不到做这两种事情的方法。

最佳答案

HashMap<u32, u64> != HashMap<i32, i64>
我知道键和值类型的大小是相同的,因此内存中的表示形式将相似,但是如果不使用不安全的,rust将不允许您在这两种类型之间进行回退和第四次转换。

以下示例不使用不安全的方法。

您应该注意,此线程中的示例在逻辑上并不合理,因为在有符号和无符号整数之间进行强制转换会产生错误的结果。

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

关于rust - 如何从枚举返回内部类型变量引用而又没有遍历泛型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60231809/

相关文章:

for-loop - 将嵌套的 for 循环转换为迭代器

rust - 调用 Clap 的 get_matches 后如何显示帮助?

rust - 使用函数指针时为 "Expected fn item, found a different fn item"

rust - 我可以使用自己的序列化结构在 Rocket 中渲染模板吗?

rust - 如何在 Rust 2018 中惯用地为箱子起别名?

rust - 我如何在 nixpkgs 派生中使用特定的 Rust 构建?

generics - 什么特征告诉编译器 `T` 是具有隐式复制的简单类型?

rust - 如何实现 TraitId?

vim - 如何将 `RustRun`重定向到Vim中的拆分缓冲区

使用rust libc::setsockopt 转换为 c_void