有没有办法将复杂条件和约束放在泛型类型上?

时间:2016-05-25 05:03:32

标签: generics rust constraints generic-programming

以伪示例的形式:

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
{}

我已经找到了一些方法来绕过这一点,但我想用一种方法来处理语言功能。

更多解释

在我的项目中,一个小型光线跟踪器,我有一个具有位置和法线的顶点类型。我有另一个顶点类型,包括位置,法线和纹理坐标。

我也有一些适用于特定顶点类型的材质类型。

我想对一些使用顶点和材料的函数进行约束:

fn do_something<V, M>(/* some arguments */) 
    -> /* some returned value */ 
where 
    if V: VertexWithTextureCoordinate 
        then M: MaterialWithTexture 
    else if V: SimpleVertex
        then M: SimpleMaterial,
{
}

这是我面临的最简单的条件。

1 个答案:

答案 0 :(得分:3)

这不容易。首先:在Rust中不存在负性状边界(说特征Foo)。有一些RFC,但AFAIK没有具体计划在不久的将来实施类似的东西。但是,专业化可以使您模拟负面特征界限。

Rust的类型系统是Turing完成的,但据我所知,如果不使用专门化和负特征边界,这两个都没有实现/不稳定,那么你想要的确切是不可能的。

你要求的是一个相当普遍的情况;有一些具体情况可能会发生。如果你有这个结构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时,您可以在A的对象上使用T的方法,当TC时,您可以在C的对象上使用T的方法,但您不能只告诉编译器“A或{ {1}}”。

解决您的具体问题

在你的情况下,我会创建另一个特征,表示顶点和材料的组合,它们可以很好地协同工作。类似的东西:

C

如您所见,我为顶点和材质的一对(元组)实现了特征。因此,您的功能将如下所示:

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

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