简体   繁体   English

为什么在 rust 中关联泛型类型的名称冲突是合法的?

[英]Why is it legal to have conflicting names for associated generic types in rust?

I find that I convert trait generics:我发现我转换了特征 generics:

trait Foo<TItem> {
    fn clone(&self, item: TItem) -> TItem { 
        item.clone() // <-- ah, it needs + Clone
    }
}

...into associated types: ...进入关联类型:

trait Foo {
    type TItem : Clone;
    fn clone(&self, item: Self::TItem) -> Self::TItem {
        item.clone()
    }
}

Reasonably often.相当频繁。 However, today I made a typo when doing this, and as a result I got:然而,今天我在这样做时犯了一个错字,结果我得到了:

trait Foo<TItem> {
    type TItem : Clone;
    fn clone(&self, item: TItem) -> TItem {
        item.clone()
    }
}

Which results in an error:这导致错误:

1 | trait Foo<TItem> {
  |           ----- method `clone` not found for this type parameter
...
item.clone()
     ^^^^^ method not found in `TItem`

In this case, the error is obvious, but in some cases, the error reporter cannot resolve the related generic, resulting in more obscure errors such as this real-world example:在这种情况下,错误是显而易见的,但在某些情况下,错误报告器无法解析相关的泛型,从而导致更隐蔽的错误,例如这个真实世界的例子:

error[E0599]: the method `clone` exists for struct `Vec<TItem>`, but its trait bounds were not satisfied
191 |         let b = matched.1.clone();
    |                           ^^^^^ method cannot be called on `Vec<TItem>` due to unsatisfied trait bounds
    |

So, long story short, yes, I know that TItem and Self::TItem are distinct types and can be referenced separately, however, what I don't understand is why this possible.所以,长话短说,是的,我知道TItemSelf::TItem是不同的类型,可以单独引用,但是,我明白的是为什么这可能。

Wouldn't it make sense for Self::X and X to share a common namespace and result in previous definition of the type 'TItem' here ? Self::XX共享一个公共名称空间并导致previous definition of the type 'TItem' here是否有意义?

Have I over looked something?我有没有看过一些东西? Is there any use-case for Self::Foo and Foo to both exist on a trait? Self::FooFoo是否都存在于一个特征上? ...or is this just a legacy thing, that exists for compatibility reasons? ...或者这只是出于兼容性原因而存在的遗留问题?

To try to make this a more specific and concrete question, the original RFC says:为了使这个问题更具体和具体, 原始 RFC说:

struct Foo { ... }

trait Bar<Input> {
    type Foo; // this is allowed
    fn into_foo(self) -> Foo; // this refers to the trait's Foo

    type Input; // this is NOT allowed <------------
}

However, this is legal now, as you can see from this play link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=92f9b44c30d44192a77cac8ffee5d142但是,现在这是合法的,从这个播放链接可以看出: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=92f9b44c30d44192a77cac8ffee5d142

What changed?什么改变了?

I'll accept any answer that can point me to an RFC / discussion that indicates why the accepted RFC is no longer correct.我会接受任何可以指向 RFC / 讨论的答案,这些答案表明为什么接受的 RFC 不再正确。

Wouldn't it make sense for Self::X and X to share a common namespace and result in previous definition of the type TItem here? Self::XX共享一个公共名称空间并导致先前在此处定义TItem类型是否有意义?

Yes, it would make sense on first glance, and that was originally the plan.是的,乍一看是有道理的,这就是最初的计划。 You can find proof of that from the RFC itself which has this example code:您可以从具有此示例代码的 RFC 本身找到证明:

trait Graph {
    type N;
    type E;
    fn has_edge(&self, &N, &N) -> bool;
}

Note how Self::N is not used.请注意如何不使用Self::N

You can also find proof of that in comments from Rust core team members such as Niko who made comments like this :您还可以在 Rust 核心团队成员(例如 Niko)的评论中找到证据,他们发表了如下评论

Instead one ought to write T , Self::T or (maximally explicit) <Self as Foo>::T .相反,应该写成TSelf::T或(最明确的) <Self as Foo>::T

Note how T is talked about as equivalent to Self::T .请注意T是如何被视为等同于Self::T的。

What changed?什么改变了?

After the RFC was accepted, it was decided to NOT allow plain T to refer to associated types, because it would lead to a lot of tricky ambiguity problems that would have to be solved.在 RFC 被接受后,决定不允许纯T引用关联类型,因为这会导致许多必须解决的棘手的歧义问题。 You can find the full discussion here , where you find this at the end (also by Niko):你可以在这里找到完整的讨论,你可以在最后找到这个(也是 Niko):

For the record I feel...fairly uncomfortable with a scoping rule that puts assoc type resolution as a fallback [to regular generic type parameters].作为记录,我觉得......对于将 assoc 类型解析作为后备 [常规泛型类型参数] 的作用域规则感到相当不舒服。

In other words: "if we allow this, we need to solve several tricky follow-up problems".换句话说:“如果我们允许这样做,我们需要解决几个棘手的后续问题”。 And then follows the decision to require Self::然后遵循要求Self::的决定

We decided at the weekly meeting today that we would stick with requiring Self:: rather than implementing this shorthand.我们在今天的周会上决定坚持要求Self::而不是实现这种速记。

Summary概括

The implementation of associated types was never "complete".关联类型的实现从未“完成”。 Instead, it has been done in smaller incremental steps.相反,它是以更小的增量步骤完成的。 See for example this comment from one of the implementors of associated types:例如,请参阅关联类型的一位实现者评论:

This patch is already huge so I'd like to implement that in a followup.这个补丁已经很大了,所以我想在后续中实现它。

Because of the incremental nature of implementing support for associated types, and because of the decision to deviate from the original RFC, it is currently possible to have both a generic type parameter named T and an associated type named T .由于实现对关联类型支持的增量性质,并且由于决定偏离原始 RFC,目前可以同时具有名为T的泛型类型参数和名为T的关联类型。

The names don't conflict.名称不冲突。 Your second code snippet doesn't compile, which illustrates that:您的第二个代码片段无法编译,这说明:

error[E0412]: cannot find type `TItem` in this scope
 --> src/lib.rs:3:27
  |
3 |     fn clone(&self, item: TItem) -> TItem {
  |                           ^^^^^ help: you might have meant to use the associated type: `Self::TItem`

The associated type is Self::TItem and the generic type is TItem .关联类型是Self::TItem ,通用类型是TItem They cannot be used interchangeably, and one cannot mean the other.它们不能互换使用,一个不能指另一个。 There is therefore no reason to disallow them to have the same name.因此,没有理由禁止它们使用相同的名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM