来自我的以下Ray Tracing in a Weekend。
在实施材料时,我选择创建特征Material
。
[我的想法是,世界上的对象将具有material
属性,并且在射线Hit
上,Hit
可以查看对象并按需索取材料。对于我的Normal
特征(遵循类似的想法),这很好用。我通过动态调度实现了所有这些,尽管我认为我已经掌握了足够多的特性边界以静态方式完成它。
在链接的代码中,您看到第13行,我正在请求实现Normal
的对象具有将返回Material
的方法。这表明现在Normal
不再有资格成为特征对象error[E0038]: the trait
Normal cannot be made into an object
[如果我从this之类的问题中正确理解,似乎由于泛型是单态的,Rust运行时不会切实泄漏material
的适当方法,因为表面上每种实现Normal
的类型可能存在一个不同的方法]?这似乎并没有引起我的兴趣,似乎与Rust运行时处于同一位置,我现在可以看看我手头的Normal
实现器,说Sphere
并说我将查看Sphere
的vtable。您能解释我在哪里错吗?
[从那里,我试图简单地与编译器抗争,然后转到静态调度。第17-21行
struct Hit<'a> {
point: Vec3,
distance: f32,
object: &'a dyn Normal,
}
成为
struct Hit<'a, T: Normal> {
point: Vec3,
distance: f32,
object: &'a T,
}
并且从那里开始,我试图在似乎没有尽头的情况下接一个洞接一个洞。
除了学习我目前的理解有哪些根本错误之外,我还可以做些其他的设计选择?
放在与Rust运行时相同的位置,我可以看一下一下我手头的Normal实现器,说一下Sphere,说我要看一下Sphere的vtable
除了Rust运行时在这里无能为力。
实际上,Rust没有“执行代码的东西”的意义上的运行时。 Rust运行时仅执行设置和清除任务,但是只要控制流在main
函数内部某个位置,您就可以自己运行(并且在no_std
环境中,甚至不存在)。因此,必须通过将vtable指针放在数据指针旁边,将每个动态分派放入该类型中-有关更多详细信息,请参见this documentation。
但是,正如您已经说过的那样,泛型是单态的,因此fn material
的每个实现都不会有一个Normal
:这些函数会有一个未知的,可能无限的族,每种实现Material
的类型。请注意“未知,可能无限”位:由于您不能泄漏私有部分,因此如果Normal
特性是公共的,则Material
特性也必须是公共的-然后没有什么会阻止您的代码用户添加您的代码未知的Material
的另一种实现,根本无法将其嵌入dyn Normal
的vtable中。
这就是通用方法不是对象安全的原因。它们无法放入特征对象vtable,因为创建特征对象时我们并不了解它们。