Как вернуть ссылку на вариант внутреннего типа из перечисления без повсеместного использования обобщений? - PullRequest
0 голосов
/ 14 февраля 2020

Вот 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

Я хотел бы иметь альтернативу, которая позволила бы мне манипулировать (и получить из) внутренний тип варианта без повсеместных обобщений.

Как предложено в Как можно избежать эффекта пульсации от изменения конкретной структуры на generi c? , если я вместо этого могу использовать 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);
}

детская площадка

Однако, поскольку это больше не enum, я больше не могу создать вариант под одним enum / struct (код выше не компилируется).

С одной стороны, я хочу иметь возможность создать одну единственную структуру / enum, которая содержит несколько сложных типов (enum), но с другой стороны я хочу иметь возможность получить / получить доступ к базовому объекту. Я не могу найти способ сделать обе вещи.

1 Ответ

1 голос
/ 15 февраля 2020

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

Я знаю, что типы ключей и значений имеют одинаковый размер, поэтому представление в памяти будет схожим, но ржавчина не позволит вам выполнить откат назад и четвертый между двумя типами без использования 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)                                    
    }                                                               
}                                                                   
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...