要求在继承特质的关联类型上添加一个特质限定。

15

我有一个继承自另一个trait Bar的trait FooBar有一个关联类型BazFooBaz进行约束,要求Baz必须实现Hoge

trait Hoge {}

trait Bar {
    type Baz;
}

trait Foo: Bar where Self::Baz: Hoge {}

然而,当我定义一个通用函数时,需要通用类型T实现Foo接口,

// [DESIRED CODE]
fn fizz<T: Foo>(buzz: T) {
    // ...
}

rustc 报错 EO277,除非我显式地约束 T:

fn fizz<T: Foo>(buzz: T) where T::Baz: Hoge {
    // ...
}

我不明白为什么需要这样做。我想要能够编写 [期望的代码]。那么最佳的方法是什么?


你知道 trait Foo where ... 在概念上是否有效吗?我看到它可以编译,所以语法没问题,但它似乎有点奇怪。 - Shepmaster
抱歉,我表述不够清楚。你的意图已经足够清晰(对于像Hoge这样的元语法变量),我主要想知道当没有泛型类型时,你是否见过用于定义trait的where子句;我不确定我是否见过。 - Shepmaster
我不确定我是否曾经有过。也许有其他方法可以实现我的意图吗? - Tsukki
1
@Shepmaster,好吧,确实有泛型类型:Self和所有关联类型本质上都是泛型类型参数,特别是 Self。虽然看起来确实很奇怪;如果这又是一种在语法上允许但对编译器没有语义意义的事情,我也不会感到惊讶(我在Rust问题跟踪器上确实看到过类似的东西)。 - Vladimir Matveev
@VladimirMatveev 你知道为什么上面的 where T::Baz: Hoge 是必要的吗? - Tsukki
显示剩余2条评论
2个回答

9

遗憾的是(或者不是),你必须重复边界。

去年我开了一个问题,认为类型检查器不一致。代码与您的类似。

@arielb1关闭了该问题,并表示这是预期行为,并给出了以下解释:

事实上,我们不希望函数隐含过多的边界,因为这可能会导致远程更改使得函数停止编译。函数基本上有三种可用的边界:
- 显式where从句中的边界 - 例如,当你有这个从句时T:B。这包括“半显式”的Sized边界。 - 显式where从句的超trait的边界 - where从句为其超trait添加边界(如trait B:AT:B边界添加了一个T:A边界)。 - 参数的生命周期属性(outlives / implicator / implied bounds)的边界。这些仅是生命周期边界,与当前问题无关。 rust-lang/rfcs#1214涉及到它们很多。
如果您的边界不在列表中,则必须明确添加它才能使用它。我想这应该是一个FAQ条目。
今天我开了一个问题,请求将这个信息添加到文档中。

3
我明白了。我强烈反对仅仅因为它可能使Rust的开发更加困难就排除一些合理预期的行为的想法。我们应该找出用户想要输入什么,并尽力匹配。 - Tsukki

4

通过在Foo中使用另一个关联类型,可以绕过这种行为,因为编译器在关联类型定义的一部分时接受隐式约束条件(playground):

trait Foo: Bar<Baz = Self::HogeBaz> {
    type HogeBaz: Hoge;
}

新的关联类型可以隐藏在辅助特质中,以避免在每个实现中都包含它。完整示例(为了清晰起见进行重命名)(playground
trait Bound {
    fn bound();
}

trait Trait {
    type Type;
}

trait BoundedTypeHelper: Trait<Type = Self::BoundedType> {
    type BoundedType: Bound;
}

impl<T> BoundedTypeHelper for T
where
    T: Trait,
    Self::Type: Bound,
{
    type BoundedType = Self::Type;
}

trait UserTrait: BoundedTypeHelper {}

fn fizz<T: UserTrait>() {
    T::Type::bound()
}

我同意你的观点,原本基于where的约束应该被视为特征定义的一部分,并隐式地应用。让关联类型的边界内联起来是有效的,但是where子句却不能这样做,感觉有些武断。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接