Jac*_*ang 52 polymorphism rust
请考虑以下代码:
trait Animal {
fn make_sound(&self) -> String;
}
struct Cat;
impl Animal for Cat {
fn make_sound(&self) -> String {
"meow".to_string()
}
}
struct Dog;
impl Animal for Dog {
fn make_sound(&self) -> String {
"woof".to_string()
}
}
fn main () {
let dog: Dog = Dog;
let cat: Cat = Cat;
let v: Vec<Animal> = Vec::new();
v.push(cat);
v.push(dog);
for animal in v.iter() {
println!("{}", animal.make_sound());
}
}
Run Code Online (Sandbox Code Playgroud)
编译器告诉我这v是Animal我尝试推送时的向量cat(类型不匹配)
那么,我如何制作属于特征的对象向量并在每个元素上调用相应的特征方法呢?
Fra*_*gné 67
Vec<Animal>是不合法的,但编译器不能告诉你,因为类型不匹配以某种方式隐藏它.如果我们删除调用push,编译器会给我们以下错误:
<anon>:22:9: 22:40 error: instantiating a type parameter with an incompatible type `Animal`, which does not fulfill `Sized` [E0144]
<anon>:22 let mut v: Vec<Animal> = Vec::new();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
这不合法的原因是在内存中连续Vec<T>存储了许多T对象.然而,Animal是一种特性,并且特征没有大小(a Cat和a Dog不保证具有相同的大小).
要解决这个问题,我们需要存储具有大小的东西Vec.最直接的解决方案是将值包装在a中Box,即Vec<Box<Animal>>.Box<T>具有固定大小(如果T是特征,则为"胖指针",否则为简单指针).
这是一个工作main:
fn main () {
let dog: Dog = Dog;
let cat: Cat = Cat;
let mut v: Vec<Box<Animal>> = Vec::new();
v.push(Box::new(cat));
v.push(Box::new(dog));
for animal in v.iter() {
println!("{}", animal.make_sound());
}
}
Run Code Online (Sandbox Code Playgroud)
at5*_*321 26
现有的答案很好地解释了问题Vec<Animal>,但它们使用旧的语法,不再有效。
简而言之,向量需要包含特征对象,并且其类型应该是 (类似) Vec<Box<dyn Animal>>。
在现代 Rust 中,dyn关键字用于指定特征对象。但我们不能只使用Vec<dyn Animal>,因为dyn Animal它没有大小(Cat并且Dog可能有不同大小的字段)。向量只能包含固定大小的元素。这就是为什么在向量中我们应该存储某种指向实际结构的指针。结构体Box就是这样的一个选项,它是一种本身具有固定大小的智能指针。
让我们测试一下(在 64 位机器上):
use std::mem::size_of;
println!("size Cat = {}", size_of::<Cat>()); // 0 bytes (the Cat struct has no fields)
println!("size Dog = {}", size_of::<Dog>()); // 0 bytes (the Dog struct has no fields)
println!("size BoxCat = {}", size_of::<Box<Cat>>()); // 8 bytes (1 usize pntr)
println!("size BoxDyn = {}", size_of::<Box<dyn Animal>>()); // 16 bytes (2 usize pointers)
println!("{}", size_of::<dyn Animal>()); // Error: doesn't have a size known at compile-time
Run Code Online (Sandbox Code Playgroud)
请注意,如果Cat有字段,size_of::<Cat>()将会超过0,但size_of::<Box<Cat>>()和size_of::<Box<dyn Animal>>()根本不会改变。
另请注意,Box<dyn Animal>实际上包含 2 个指针:
现在以你的例子为例。要使其工作,您只需替换这三行:
let v: Vec<Animal> = Vec::new();
v.push(cat);
v.push(dog);
Run Code Online (Sandbox Code Playgroud)
用这些:
let mut v: Vec<Box<dyn Animal>> = Vec::new();
v.push(Box::new(cat));
v.push(Box::new(dog));
Run Code Online (Sandbox Code Playgroud)
nat*_*ate 15
您可以使用引用特征对象&Animal借用元素并将这些特征对象存储在a中Vec.然后,您可以枚举它并使用特征的界面.
Vec通过&在特征前面添加a 来改变通用类型将起作用:
fn main() {
let dog: Dog = Dog;
let cat: Cat = Cat;
let mut v: Vec<&Animal> = Vec::new();
// ~~~~~~~
v.push(&dog);
v.push(&cat);
for animal in v.iter() {
println!("{}", animal.make_sound());
}
// Ownership is still bound to the original variable.
println!("{}", cat.make_sound());
}
Run Code Online (Sandbox Code Playgroud)
如果您希望原始变量保留所有权并在以后重复使用,那么这非常有用.
请记住上面的场景,您不能转移所有权,dog或者cat因为Vec已经在同一范围内借用了这些具体实例.
引入新范围可以帮助处理特定情况:
fn main() {
let dog: Dog = Dog;
let cat: Cat = Cat;
{
let mut v: Vec<&Animal> = Vec::new();
v.push(&dog);
v.push(&cat);
for animal in v.iter() {
println!("{}", animal.make_sound());
}
}
let pete_dog: Dog = dog;
println!("{}", pete_dog.make_sound());
}
Run Code Online (Sandbox Code Playgroud)