繁体   English   中英

被 Vec 迷惑了<Box<dyn T> &gt; 和 TypeId 行为

[英]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 好吧, Selfdyn 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> ,实际上TypeIdTypeId::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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM