将特征对象重新装箱为一般实现的特征

Re-boxing trait objects to generically implemented 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,而是在所有类型上普遍实现更通用的特征实现 SpecificTrait.

我知道特征对象类型不同(这导致了上面代码中的错误),但我希望类型推断能够确认每个 dyn SpecificTrait 对象也应该可以表示为 dyn GeneralTrait 对象。但是,我也不能简单地使用 Box<dyn SpecificTrait> as Box<dyn GeneralTrait>

那么,我将如何(idomatically)将我的 Box<dyn SpecificTrait> 重新表达为 Box<dyn GeneralTrait>

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

I would expect type inference to acknowledge that every dyn SpecificTrait object should also be expressable as a dyn GeneralTrait object

但事实并非如此。一个dyn SpecificTrait包含指向SpecificTrait方法的函数指针的“virtualtable”的指针,而你无法获得指向相应virtualtable的指针forGeneralTrait 从它。 One of answers to the question you linked 详细解释了子特征的问题,但是

implements the more general trait generically over all types that implement SpecificTrait

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

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

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 {})
    }
}

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

Playground