有什么办法可以对泛型设置复杂的条件和约束吗?

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,
{
    // ...
}

如果你考虑一下,无论如何你都必须写两个实现:当TA时,你可以在[=16=的对象上使用A的方法], 当 TC 时,你可以在 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,
{ /* ... */ }

这应该工作得很好;但是,它可能不是您的光线追踪器的最佳解决方案...