Как привести Vec структур к Vec черт объектов? - PullRequest
1 голос
/ 03 ноября 2019

Попытка создать структуру БД, представляющую собой HashMap векторов. Каждый Vec содержит Box<dyn Model>.

use std::collections::HashMap;

trait Model {
    fn id(&self) -> i32;
}

struct User;
struct Message;

impl Model for User {
    fn id(&self) -> i32 { 4 }
}

impl Model for Message {
    fn id(&self) -> i32 { 3 }
}

struct DB {
    users: Vec<Box<User>>,
    messages: Vec<Box<Message>>,
    tables: HashMap<String, Vec<Box<dyn Model>>>,
}

impl DB {
    fn new() -> Self {
        let users: Vec<Box<User>> = Vec::new();
        let messages: Vec<Box<Message>> = Vec::new();
        let mut tables: HashMap<String, Vec<Box<dyn Model>>> = HashMap::new();
        tables.insert("users".to_string(), users);
        tables.insert("messages".to_string(), messages);
        Self {
            users,
            messages,
            tables,
        }
    }
}

Компилятор выдает следующую ошибку:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/lib.rs:37:44
   |
37 |         tables.insert("users".to_string(), users);
   |                                            ^^^^^ expected trait Model, found struct `User`
   |
   = note: expected type `std::vec::Vec<std::boxed::Box<dyn Model>>`
              found type `std::vec::Vec<std::boxed::Box<User>>`

error[E0308]: mismatched types
  --> src/lib.rs:38:47
   |
38 |         tables.insert("messages".to_string(), messages);
   |                                               ^^^^^^^^ expected trait Model, found struct `Message`
   |
   = note: expected type `std::vec::Vec<std::boxed::Box<dyn Model>>`
              found type `std::vec::Vec<std::boxed::Box<Message>>`

Почему компилятор не может сделать вывод, что User и Message реализуют Model?

1 Ответ

3 голосов
/ 03 ноября 2019

Типы Box<dyn Model> и Box<User> не являются взаимозаменяемыми. Коллекции, содержащие одну, не могут быть непосредственно преобразованы в другую, даже с небезопасным кодом. Эти типы разные и имеют разные представления в памяти. Они даже имеют разные размеры:

println!("{}", std::mem::size_of::<Box<User>>());      // 8
println!("{}", std::mem::size_of::<Box<dyn Model>>()); // 16

Единственный способ конвертировать из Vec<Box<User>> в Vec<Box<dyn Model>> - это поэтапно. Каждый предмет должен быть приведен в соответствие с этим:

let model: Box<dyn Model> = user;

Или:

let model = Box::<dyn Model>::from(user);

В результате получается эта уродливая вещь:

tables.insert(
    "users".to_string(),
    users
        .iter()
        .map(|user| Box::<dyn Model>::from(user))
        .collect()
);

Если вам не нужноИсходный вектор после этого, вы избегаете клонирования, делая его изменчивым и сливая вместо этого:

tables.insert(
    "users".to_string(),
    users
        .drain(..)
        .map(|user| Box::<dyn Model>::from(user))
        .collect(),
);
...