[英]How does Rust compile this example with cyclic trait bounds?
I'm having trouble understanding how the following example, distilled from this code , compiles:我无法理解从这段代码中提取的以下示例如何编译:
trait A: B {}
trait B {}
impl<T> B for T where T: A {}
struct Foo;
impl A for Foo {}
fn main() {}
My current understanding is that我目前的理解是
trait A: B
declares a trait A with the supertrait B. The Rust reference on traits states trait A: B
用超特征 B 声明了一个特征 A。Rust 对特征的引用指出Supertraits are traits that are required to be implemented for a type to implement a specific trait.
Supertraits 是为实现特定 trait 的类型需要实现的特征。
impl<T> B for T where T:A
implements B for any type with the trait A . impl<T> B for T where T:A
为具有特征 A 的任何类型实现 B 。 I expect impl A for Foo
to fail because before A is implemented for Foo, the blanket implementation can't implement B for Foo, which is required.我希望
impl A for Foo
会失败,因为在为 Foo 实现 A 之前,一揽子实现不能为 Foo 实现 B,这是必需的。
My most plausible model for what rustc does while compiling the snippet is as follows:对于 rustc 在编译代码段时所做的工作,我最合理的模型如下:
A: B
A: B
Is this in some way close to the truth?这在某种程度上接近事实吗? Is there any documentation I missed explaining the order in which implementations are processed?
是否有任何我错过的文档来解释处理实现的顺序?
rustc doesn't work "in order". rustc 不能“按顺序”工作。 Rather, we first register all impls and then type-check each impl with no particular order.
相反,我们首先注册所有 impl,然后对每个 impl 进行类型检查,没有特定的顺序。 The idea is that we collect a list of obligations (of various kinds - one of them is a trait bound), and then we match them against
impl
s (not just; this is only one way to resolve an obligation, but this is what relevant here).这个想法是我们收集义务列表(各种类型 - 其中一个是 trait bound),然后我们将它们与
impl
匹配(不仅仅是;这只是解决义务的一种方法,但这就是此处相关)。 Each obligation can create another, recursive obligations and we elaborate them until there are no more.每个义务都可以创建另一个递归义务,我们会详细说明它们,直到不再有。
The way it currently works is that when we check an impl Trait for Type
, we add an obligation Type: Trait
.它目前的工作方式是,当我们检查
impl Trait for Type
,我们添加了一个义务Type: Trait
。 This might seem silly, but we later elaborate it further until all required bounds are met.这可能看起来很愚蠢,但我们稍后会进一步详细说明,直到满足所有要求的界限。
So let's say we're currently checking impl<T> B for T where T: A
.因此,假设我们目前正在检查
impl<T> B for T where T: A
。 We add one obligation, T: B
, and match it against impl B for T
.我们添加一项义务
T: B
,并将其与impl B for T
匹配。 There is nothing to elaborate further, so we finish successfully.没有什么可以进一步阐述的,所以我们成功地完成了。
We then check impl A for Foo
, and add an obligation Foo: A
.然后我们检查
impl A for Foo
,并添加一个义务Foo: A
。 Since the trait A
requires Self: B
, we add another obligation Foo: B
.由于特征
A
需要Self: B
,我们添加另一个义务Foo: B
。 Then we start matching obligations: the first obligation, Foo: A
, is matched by the currently processed impl
with no additional obligations.然后我们开始匹配义务:第一个义务
Foo: A
与当前处理的impl
匹配,没有额外的义务。 The second obligation, Foo: B
, is matched against impl<T> B for T where T: A
.第二个义务
Foo: B
与impl<T> B for T where T: A
匹配。 This has a new obligate - T: A
or Foo: A
- so we try to match that.这有一个新的义务 -
T: A
或Foo: A
- 所以我们尝试匹配它。 We successfully match that against impl A for Foo
, with no additional obligations.我们成功地
impl A for Foo
匹配,没有额外的义务。
An interesting implication of the above is that if we change the second impl
to the following:上面的一个有趣的含义是,如果我们将第二个
impl
更改为以下内容:
impl A for Foo where Foo: B {}
Then this no longer compile with a "overflow evaluating the requirement Foo: A
" error ( playground ), even though it is essentially the same, because now to prove that Foo: A
rustc needs to prove that Foo: B
and again that Foo: A
, while previously it just registered an obligation for Foo: B
and not proved it immediately.然后这不再与“评估要求
Foo: A
的溢出”错误( 操场)编译,即使它本质上是相同的,因为现在要证明Foo: A
rustc 需要证明Foo: B
并再次证明Foo: A
,而之前它只是为Foo: B
注册了一项义务,并没有立即证明它。
Note: The above is an over-simplification: for example, there is also a cache, and well-formed obligations, and much more.注意:以上是过度简化:例如,还有一个缓存,以及格式良好的义务,等等。 But the general principle is the same.
但一般原理是一样的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.