【问题标题】:How to coerce a Vec of structs to a Vec of trait objects?如何将结构的 Vec 强制为特征对象的 Vec?
【发布时间】:2020-02-29 04:28:23
【问题描述】:

尝试创建一个由向量​​组成的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>>`

为什么编译器不能推断出UserMessage 实现了Model

【问题讨论】:

  • Box&lt;dyn Model&gt;Box&lt;User&gt; 是不相关的类型。事实上,类型有不同的大小,所以你不能零成本将一个集合转换为另一个集合。

标签: rust traits trait-objects


【解决方案1】:

Box&lt;dyn Model&gt;Box&lt;User&gt; 类型不可互换。包含一个的集合不能直接转换成另一个,即使是不安全的代码。这些类型是不同的,并且在内存中具有不同的表示形式。它们甚至有不同的尺寸:

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

Vec&lt;Box&lt;User&gt;&gt; 转换为Vec&lt;Box&lt;dyn Model&gt;&gt; 的唯一方法是逐项进行。每个项目都需要像这样强制:

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(),
);

【讨论】:

  • .map 行也可以简单地是:.map(Box::&lt;dyn Model&gt;::from)
猜你喜欢
  • 1970-01-01
  • 2020-10-02
  • 2020-09-25
  • 2021-05-11
  • 1970-01-01
  • 2021-03-16
  • 1970-01-01
  • 2020-03-02
相关资源
最近更新 更多