问题
Trying to create a DB struct that is a HashMap
of vectors. Each Vec
contains 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,
}
}
}
The compiler produces the following error:
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>>`
Why can't the compiler infer that User
and Message
implement Model
?
回答1:
The types Box<dyn Model>
and Box<User>
are not interchangeable. Collections containing one cannot be directly transformed into the other, even with unsafe code. These types are different and have different representations in memory. They even have different sizes:
println!("{}", std::mem::size_of::<Box<User>>()); // 8
println!("{}", std::mem::size_of::<Box<dyn Model>>()); // 16
The only way to convert from Vec<Box<User>>
to Vec<Box<dyn Model>>
is on an item-by-item basis. Each item needs to be coerced like this:
let model: Box<dyn Model> = user;
Or:
let model = Box::<dyn Model>::from(user);
Resulting in this ugly thing:
tables.insert(
"users".to_string(),
users
.iter()
.map(|user| Box::<dyn Model>::from(user))
.collect()
);
If you don't need the original vector after this, you avoid cloning by making it mutable and draining instead:
tables.insert(
"users".to_string(),
users
.drain(..)
.map(|user| Box::<dyn Model>::from(user))
.collect(),
);
来源:https://stackoverflow.com/questions/58683548/how-to-coerce-a-vec-of-structs-to-a-vec-of-trait-objects