4

我正在阅读特征对象需要对象安全,但我不理解泛型类型参数的问题。

使用 trait 时用具体类型参数填充的泛型类型参数也是如此:具体类型成为实现 trait 的类型的一部分。当通过使用 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})]
    };
}

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

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

4

2 回答 2

6

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

一个不起作用的例子是类型参数是方法的一部分:

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

当一个函数有一个类型参数时,Rust 会为每个实际调用它的类型对函数进行单态化(制作一个新副本)。这与 trait 对象不兼容,因为 Rust 直到运行时才知道该方法属于哪个 impl。

于 2019-07-29T11:29:17.460 回答
6

正如@PeterHall提到的,使用通用方法,特征不能是对象安全的。

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

通过使用虚拟表来实现对特征方法的正确实现的有效分派,虚拟表本质上是一个指向函数的表。特征对象中的每个方法都在虚拟表中获得一个插槽来存储一个指向函数的指针。

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

这意味着不可能有一个指向函数的指针,fn foo<T>() -> T;因为它没有代码,相反,你可能有一个指向函数的指针,一个指向函数的指针fn foo<i32>() -> i32另一个指向函数的指针fn foo<String>() -> String。 ..

对于泛型方法,不可能有一个指向函数的指针,因此也不可能有一个 v-table 条目,因此无法通过运行时调度调用该方法,也就是说,在dyn Trait.

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

于 2019-07-29T14:31:08.107 回答