[英]Rust: can't collect an `Iterator<Item = Box<Dog>>` into `Vec<Box<dyn Animal>>`
[英]Bewildered by Vec<Box<dyn T>> and TypeId behaviour
use std::any::Any;
use std::any::TypeId;
trait Task {}
struct SomeTask;
impl Task for SomeTask {}
fn main() {
let tasks_a: Vec<Box<SomeTask>> = vec![Box::new(SomeTask)];
let tasks_b: Vec<Box<dyn Any>> = vec![Box::new(SomeTask)];
let tasks_c: Vec<Box<dyn Task>> = vec![Box::new(SomeTask)];
println!(
"Item stored in `Vec<Box<SomeTask>>` is of type `SomeTask`? {}",
TypeId::of::<SomeTask>() == (&*tasks_a[0]).type_id(),
);
println!(
"Item stored in `Vec<Box<dyn Any>>` is of type `SomeTask`? {}",
TypeId::of::<SomeTask>() == (&*tasks_b[0]).type_id(),
);
println!(
"Item stored in `Vec<Box<dyn Task>>` cast as `&dyn Any` is of type `SomeTask`? {}",
TypeId::of::<SomeTask>() == (&tasks_c[0] as &dyn Any).type_id(),
);
println!(
"Item stored in `Vec<Box<dyn Task>>` is of type `SomeTask`? {}",
TypeId::of::<SomeTask>() == (&*tasks_c[0]).type_id(),
);
}
( 游乐场)
输出:
Item stored in `Vec<Box<SomeTask>>` is of type `SomeTask`? true
Item stored in `Vec<Box<dyn Any>>` is of type `SomeTask`? true
Item stored in `Vec<Box<dyn Task>>` cast as `&dyn Any` is of type `SomeTask`? false
Item stored in `Vec<Box<dyn Task>>` is of type `SomeTask`? false
你能帮忙解释一下输出吗? 具体来说,我不明白为什么存储在Vec<Box<dyn Any>>
中的T
项目的类型是T
,但存储在Vec<Box<dyn Trait>>
中的项目并非如此。
当您调用(&*tasks_b[0]).type_id()
时,您调用<dyn Any as Any>::type_id(&self)
。 这通过动态 vtable 调用dyn Any
上的type_id()
方法。
当您调用(&*tasks_a[0]).type_id()
时,您调用<SomeTask as Any>::type_id(&self)
。 这会静态调用此函数,因为类型是已知的。
关键是(&*tasks_c[0]).type_id()
调用。 &*tasks[0]
属于&dyn Task
类型。 此类型没有定义动态type_id()
方法,因为它不是dyn Any
。 因此,与静态SomeTask
相同,我们调用静态type_id()
。 什么是Self
? 好吧, Self
是dyn Task
。 这是一个具体的类型 - dyn Trait
不仅仅是实现该 trait 的某种类型,它本身就是一个表示实现该 trait 的类型擦除类型的类型。 就像任何其他类型一样,它有一个TypeId
。 如果您将(&*tasks_c[0]).type_id()
的结果与TypeId::of::<dyn Task>()
进行比较,您会发现它们是相等的。
(&tasks_c[0] as &dyn Any).type_id()
类似,但不完全相同:这里我们采用&tasks_c[0]
,其类型&Box<dyn Task>
,并将其强制为&dyn Any
- 也就是说,擦除的类型是Box<dyn Task>
,实际上TypeId
是TypeId::of::<Box<dyn Task>>
。
如果您希望dyn Task
上的type_id()
计算已擦除类型的实际类型 ID,则可以将Any
设为Task
的超特征。 但是,这将不允许您将dyn Task
变成dyn Any
。 这需要不稳定的特质向上转换强制。 在那之前,通常的方法是在 trait 中公开一个将dyn Task
向上转换为dyn Any
的方法:
trait Task: Any {
fn as_any(&self) -> &dyn Any;
}
impl Task for SomeTask {
fn as_any(&self) -> &dyn Any { self as &dyn Any }
}
tasks_c[0].as_any().type_id();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.