有什么办法可以对泛型设置复杂的条件和约束吗?
Is there any way to put complex conditions and constraints on a generic type?
以伪示例的形式:
trait A {}
trait B {}
trait C {}
struct D<T, X>
where
if T: A
then X: is not B
else if X: B
then T: C
{}
我已经找到了绕过这个的方法,但我想要一种利用语言特性来完成的方法。
更多说明
在我的项目中,一个小型光线追踪器,我有一个顶点类型,它有一个位置和一个法线。我有另一种具有位置、法线和纹理坐标的顶点类型。
我还有一些 material 类型可用于特定顶点类型。
我想对一些同时使用顶点和material的函数做一个约束:
fn do_something<V, M>(/* some arguments */)
-> /* some returned value */
where
if V: VertexWithTextureCoordinate
then M: MaterialWithTexture
else if V: SimpleVertex
then M: SimpleMaterial,
{
}
这是我遇到的最简单的情况。
没那么容易。首先:Rust 中尚不存在负特征边界(即 not 特征 Foo
)。有一些 RFC,但 AFAIK 没有具体计划在不久的将来实施类似的东西。但是,专业化可以让您模拟负面特征界限。
Rust 的类型系统是图灵完备的,但据我所知,如果不使用专业化和负特征边界,这两者都不是 implemented/not 稳定的,那么你想要的东西是不可能的。
你问的是一个很一般的案例;在某些特定情况下是可能的。如果你有这个结构 D
并且想向它添加需要这些特征边界的方法,你可以只写两个 impl
块:
impl<T, X> D<T, X>
where
T: A,
X: NotB, // assume `NotB` is just another trait
{
// ...
}
impl<T, X> D<T, X>
where
T: C,
X: B,
{
// ...
}
如果你考虑一下,无论如何你都必须写两个实现:当T
是A
时,你可以在[=16=的对象上使用A
的方法], 当 T
是 C
时,你可以在 T
的对象上使用 C
的方法,但你不能只告诉编译器 "either A
or C
".
针对您的特定问题的解决方案
在你的情况下,我会创建另一个特征,表示顶点和 material 的组合,它们可以很好地协同工作。类似于:
trait VertexMaterialPair {
// ...
}
impl<V, M> VertexMaterialPair for (V, M)
where
V: VertexWithTextureCoordinate,
M: MaterialWithTexture,
{ /* ... */ }
impl<V, M> VertexMaterialPair for (V, M)
where
V: SimpleVertex,
M: SimpleMaterial,
{ /* ... */ }
如您所见,我为顶点和 material 的一对(元组)实现了特征。因此你的函数看起来像:
fn do_something<V, M>(/* some arguments */) -> /* ... */
where
(V, M): VertexMaterialPair,
{ /* ... */ }
这应该工作得很好;但是,它可能不是您的光线追踪器的最佳解决方案...
以伪示例的形式:
trait A {}
trait B {}
trait C {}
struct D<T, X>
where
if T: A
then X: is not B
else if X: B
then T: C
{}
我已经找到了绕过这个的方法,但我想要一种利用语言特性来完成的方法。
更多说明
在我的项目中,一个小型光线追踪器,我有一个顶点类型,它有一个位置和一个法线。我有另一种具有位置、法线和纹理坐标的顶点类型。
我还有一些 material 类型可用于特定顶点类型。
我想对一些同时使用顶点和material的函数做一个约束:
fn do_something<V, M>(/* some arguments */)
-> /* some returned value */
where
if V: VertexWithTextureCoordinate
then M: MaterialWithTexture
else if V: SimpleVertex
then M: SimpleMaterial,
{
}
这是我遇到的最简单的情况。
没那么容易。首先:Rust 中尚不存在负特征边界(即 not 特征 Foo
)。有一些 RFC,但 AFAIK 没有具体计划在不久的将来实施类似的东西。但是,专业化可以让您模拟负面特征界限。
Rust 的类型系统是图灵完备的,但据我所知,如果不使用专业化和负特征边界,这两者都不是 implemented/not 稳定的,那么你想要的东西是不可能的。
你问的是一个很一般的案例;在某些特定情况下是可能的。如果你有这个结构 D
并且想向它添加需要这些特征边界的方法,你可以只写两个 impl
块:
impl<T, X> D<T, X>
where
T: A,
X: NotB, // assume `NotB` is just another trait
{
// ...
}
impl<T, X> D<T, X>
where
T: C,
X: B,
{
// ...
}
如果你考虑一下,无论如何你都必须写两个实现:当T
是A
时,你可以在[=16=的对象上使用A
的方法], 当 T
是 C
时,你可以在 T
的对象上使用 C
的方法,但你不能只告诉编译器 "either A
or C
".
针对您的特定问题的解决方案
在你的情况下,我会创建另一个特征,表示顶点和 material 的组合,它们可以很好地协同工作。类似于:
trait VertexMaterialPair {
// ...
}
impl<V, M> VertexMaterialPair for (V, M)
where
V: VertexWithTextureCoordinate,
M: MaterialWithTexture,
{ /* ... */ }
impl<V, M> VertexMaterialPair for (V, M)
where
V: SimpleVertex,
M: SimpleMaterial,
{ /* ... */ }
如您所见,我为顶点和 material 的一对(元组)实现了特征。因此你的函数看起来像:
fn do_something<V, M>(/* some arguments */) -> /* ... */
where
(V, M): VertexMaterialPair,
{ /* ... */ }
这应该工作得很好;但是,它可能不是您的光线追踪器的最佳解决方案...