你不能像在 C++ 中专门化模板那样在 Rust 中专门化泛型。 (Rust 有一个特性叫做“专业化”,但它只适用于impl
s,这里并不真正相关。)Rust 泛型有时被称为“有原则的”,因为它们必须工作原则(声明后),不仅仅是在实践中(一旦实例化)。这是一个刻意的选择Rust 的设计者为了避免 C++ 中 SFINAE 的一些更混乱的后果。
我可以想到两种主要方法来实现与 Rust 中的 C++ 代码类似的效果,具体取决于代码的通用上下文。一种方法是使用特征作为类型级别函数来计算参数化结构的内容类型,这与 C++ 版本类似,但具有稍微更详细的字段访问(为简单起见,我会想象T
is f32
对于这些示例):
// types that contain the actual data
struct Vector2 {
x: f32,
y: f32,
}
struct Vector3 {
x: f32,
y: f32,
z: f32,
}
// types that will be used to parameterize a type constructor
struct Fixed<const N: usize>;
struct Dynamic;
// a type level function that says what kind of data corresponds to what type
trait VectorSize {
type Data;
}
impl VectorSize for Fixed<2> {
type Data = Vector2;
}
impl VectorSize for Fixed<3> {
type Data = Vector3;
}
impl VectorSize for Dynamic {
type Data = Vec<f32>;
}
// pulling it all together
struct Vector<Z>(Z::Data) where Z: VectorSize;
现在,如果你有v: Vector<Fixed<2>>
您可以使用v.0.x
or v.0.y
,而如果你有一个Vector<Dynamic>
你必须使用v.0[0]
and v.0[1]
。但是没有办法编写一个使用的通用函数x
and y
这将适用于Vector<Fixed<2>>
or Vector<Fixed<3>>
;因为它们之间没有语义关系x
s and y
s,那是没有原则的。
另一种选择是将数组放入Vector
并制作x
and y
访问元素 0 和 1 的便捷方法:
struct Vector<const N: usize> {
xs: [f32; N],
}
impl<const N: usize> Vector<N> {
fn x(&self) -> f32 where Self: SizeAtLeast<2> {
self.xs[0]
}
fn y(&self) -> f32 where Self: SizeAtLeast<2> {
self.xs[1]
}
fn z(&self) -> f32 where Self: SizeAtLeast<3> {
self.xs[2]
}
}
// In current Rust, you can't bound on properties of const generics, so you have
// to do something like this where you implement the trait for every relevant
// number. Macros can make this less tedious. In the future you should be able to
// simply add bounds on `N` to `x`, `y` and `z`.
trait SizeAtLeast<const N: usize> {}
impl SizeAtLeast<2> for Vector<2> {}
impl SizeAtLeast<2> for Vector<3> {}
impl SizeAtLeast<2> for Vector<4> {}
impl SizeAtLeast<3> for Vector<3> {}
impl SizeAtLeast<3> for Vector<4> {}
现在您可以编写适用于的通用函数Vector<N>
并使用x
and y
,但要适应它以允许突变并不容易。一种方法是添加x_mut
, y_mut
and z_mut
返回的方法&mut f32
.
相关问题