rust 将绑定的关联类型用作函数参数时未强制执行

inb24sb2  于 2023-01-26  发布在  其他
关注(0)|答案(1)|浏览(115)

在下面的示例代码中,trait Foo要求关联的类型X实现Clone trait。
当在do_it函数签名中使用impl Foo<X = Baz>语法时,cargo check不会抱怨Baz没有实现Clone特征。
然而,cargo check确实在impl Foo for Bar块中抱怨了这个问题。
我本以为impl Foo<X = Baz>也会以同样的方式抱怨。

trait Foo {
    type X: Clone;
}

struct Bar;

struct Baz;

impl Foo for Bar {
    type X = Baz; // <- complains Baz does not impl Clone trait
}

fn do_it(foo: impl Foo<X = Baz>) {} // <- does not complain

如果X是泛型参数,则情况并非如此。在这种情况下,cargo check表示foo: impl Foo<Bar>不满足Clone特性界限

trait Foo<X>
where
    X: Clone,
{
}

struct Bar;

struct Baz;

fn do_it(foo: impl Foo<Baz>) {} // <- complains Baz does not impl Clone trait

这是预期行为吗?如果是,为什么?

p8h8hvxi

p8h8hvxi1#

这在介绍关联类型的RFC中用一个简短的句子进行了描述:
关联类型上的BOUNDSWHERE_CLAUSE是trait实现者的 * 义务 *,以及trait用户的 * 假设 *:

trait Graph {
    type N: Show + Hash;
    type E: Show + Hash;
    ...
}

impl Graph for MyGraph {
    // Both MyNode and MyEdge must implement Show and Hash
    type N = MyNode;
    type E = MyEdge;
    ...
}

fn print_nodes<G: Graph>(g: &G) {
    // here, can assume G::N implements Show
    ...
}

这意味着负责证明边界成立的人不是trait的用户(在你的例子中是do_it()),而是trait的实现者,这与trait的泛型参数相反,后者的证明义务在用户身上。
当您查看时,差异应该是显而易见的:对于泛型参数,类型是外来的,在trait实现中是未知的,所以它必须假设边界漏洞。另一方面,trait的用户有具体的类型(即使它们本身是泛型的,从trait的Angular 来看,它们仍然是具体的类型),所以它应该证明边界成立。相反,对于关联类型,情况就不同了:实现者知 prop 体的类型,而用户则假设一个泛型类型(即使像你的代码一样,它把它们限制在一个特定的类型,在一般情况下,它仍然是未知的)。
请注意,在关联类型(type Foo where Self::Foo: Clone)上使用 where bounds,这是与泛型关联类型一起引入的(是的,我知道我链接的RFC带来了它们,但据我所知,它们没有实现,最终作为具有不同语义的GAT的一部分实现),情况再次不同于正常的关联类型边界:用户也必须证明它们(我认为两者都需要证明,但我不确定),这是因为它们被期望用于关联类型的泛型参数,因此它们类似于traits的泛型参数或其中的where子句。

相关问题