将 trait 对象重新装箱为通用实现的 trait

问题描述

到目前为止,我很少遇到 Rust 的类型推断问题,但我担心我不太了解以下代码的问题:

trait SpecificTrait {}

struct SpecificStruct;

impl SpecificTrait for SpecificStruct {}

trait GeneralTrait {}

impl<T: SpecificTrait> GeneralTrait for T {}

fn new_specific_Box() -> Box<dyn SpecificTrait> {
    Box::new(SpecificStruct {})
}

fn new_general_Box(from_specific_Box: bool) -> Box<dyn GeneralTrait> {
    if from_specific_Box {
        new_specific_Box()
    } else {
        Box::new(SpecificStruct {})
    }
}

Playground

我认为它与 Rust 可能仍然 not supporting upcasting 有关,尽管在此代码SpecificTrait 不需要 GeneralTrait,而是在所有实现的类型上实现了更通用的 trait SpecificTrait

我知道 trait 对象类型是不同的(这会导致上面代码中的错误),但我希望类型推断承认每个 dyn SpecificTrait 对象也应该表示为 {{1 }} 目的。但是,我也不能简单地投射 dyn GeneralTrait

那么,我将如何(习惯性地)将我的 Box<dyn SpecificTrait> as Box<dyn GeneralTrait> 重新表达为 Box<dyn SpecificTrait>

解决方法

我希望类型推断承认每个 dyn SpecificTrait 对象也应该可以表示为 dyn GeneralTrait 对象

但事实并非如此。 dyn SpecificTrait 包含指向 SpecificTrait 方法的函数指针的“虚拟表”的指针,您无法从中获取指向 GeneralTrait 的相应虚拟表的指针。 One of answers to the question you linked 详细解释了子特征的问题,但是

在所有实现 SpecificTrait 的类型上实现更通用的特征

让情况变得更糟。对于子特性,超特性的方法至少存在于子特性 vtable 中(该答案中的 24- |methods of Self and supertraits)。一揽子实施则不然。

,

另一个答案解释了为什么你不能简单地转换 trait 对象来获得你想要的结果。但是,有一个解决方法:

impl SpecificTrait for Box<dyn SpecificTrait>{}
fn new_general_box(from_specific_box: bool) -> Box<dyn GeneralTrait> {
    if from_specific_box {
        Box::new(new_specific_box())
    } else {
        Box::new(SpecificStruct {})
    }
}

换句话说,只需为装箱的 trait 对象实现您的特定 trait,然后将其装箱即可。不是最有效的,但它会起作用。

Playground

,

也许我没有得到你想要表达的更深层次的问题,但从代码来看,它不能编译的原因是因为函数 fn new_specific_box() -> Box<dyn SpecificTrait> 的返回类型是 Box<dyn SpecificTrait> 其中作为你期待Box<dyn GeneralTrait>。这两个是不同的类型,所以这段代码不会编译。如果能匹配到类型,那么编译应该没问题。