在特征对象中使用泛型类型参数会引起什么问题?

时间:2019-07-29 11:15:32

标签: generics rust traits trait-objects

我正在阅读Object Safety Is Required for Trait Objects,但我不理解泛型类型参数的问题。

  

使用特征时,用具体类型参数填充的泛型类型参数也是如此:具体类型成为实现特征的类型的一部分。如果通过使用trait对象而忘记了类型,则无法知道用哪种类型来填充通用类型参数。

我正在尝试编写示例,但我无法理解。 什么的通用类型参数?

我试图用参数化的特征制作一个特征对象,但是一旦给了该参数一个具体的值,它就可以正常工作:

trait Creator<T> {
    fn create(&self) -> T;
}

struct CreationHouse {
    creators: Vec<Box<dyn Creator<u32>>>
}

struct NumCreator { seed: u32 }

impl Creator<u32> for NumCreator {
    fn create(&self) -> u32 {
        return self.seed;
    }
}

fn main() {
    let ch = CreationHouse{
        creators: vec![Box::new(NumCreator{seed: 3})]
    };
}

(编译良好,除了“未使用”警告)

我不明白的是,“使用特质时用具体类型参数填充的通用类型参数”是什么意思,以及通用类型如何丢失(因为特质“随身携带”它们) )。如果您能写出段落中描述的案例的例子,我将不胜感激。

2 个答案:

答案 0 :(得分:4)

  

“使用特征时使用具体类型参数填充的泛型类型参数”是什么意思

当type参数是方法的一部分时,一个不起作用的示例:

trait Foo {
    fn foo<T>(t: T) {}
}

当一个函数具有类型参数时,Rust将针对实际使用的每种类型对函数进行单态化(创建新副本)。这与特征对象不兼容,因为Rust在运行时才知道该方法属于哪个隐含对象。

答案 1 :(得分:3)

@PeterHall所述,使用通用方法,特征不能是对象安全的。

事实证明,原因仅仅是实现限制。

通过使用虚拟表可以有效地调度trait方法的正确实现,该表实际上是指向函数的指针的表。特质对象中的每个方法都会在虚拟表中获得一个插槽,以存储一个函数指针。

另一方面,泛型函数或方法根本不是函数或方法。通过用通用的实际参数替换通用参数来创建任意数量的功能或方法是一个蓝图。

这意味着无法为fn foo<T>() -> T;指向功能,因为它没有 code ,而您可能具有指向该功能的指针一个fn foo<i32>() -> i32,以及另一个{em> 指向功能的指针fn foo<String>() -> String和另一个...

对于通用方法,由于不可能具有指向函数的指针,因此无法具有v表条目,因此无法通过运行时调度(即在dyn Trait上)调用该方法。

值得注意的是,出于相同的原因,其他语言也受到相同的限制;例如,C ++也不能具有模板虚拟方法。