即将到来的错误形态

2016年8月10日 · Sophia June Turner

Rust 世界正在发生变化。如果你尝试过最新的 nightly 版本,你会注意到有些地方有点不同。在过去的几个月里,我们一直在研究一种新的错误报告方式,这种方式更容易阅读和理解。这是为了全面提高 Rust 可用性而正在进行的活动的一部分。我们提到了帮助我们进行新错误过渡的方法,并且已经有很多人加入了进来(感谢这些志愿者!)

让我们深入了解一下发生了什么变化。我们将从一个简单的例子开始

fn borrow_same_field_twice_mut_mut() {
    let mut foo = make_foo();
    let bar1 = &mut foo.bar1;
    let _bar2 = &mut foo.bar1;
    *bar1;
}

在这里,我们犯了两次可变借用同一个值的错误。这是 Rust 中一个经典的错误。果然,之前的编译器给出的错误信息也差不多是这个意思

Picture of old error style

但问题是,需要花费几秒钟的时间来查看消息、定位自己并找到关键部分。这种时间损失会累积起来。如果,相反,我们清除所有会减慢您阅读错误消息速度的东西呢?

这是新的错误格式。它的设计基于一个基本观察,即错误应该专注于你编写的代码。通过这样做,你可以更容易地看到正在发生的事情的上下文。

设计

关键的见解是将你的源代码放在最前面和中心位置 - 你在输出中看到的一切都建立在你的代码之上。通过使用你编写的代码作为上下文,我们让你很容易一眼就能知道问题发生的位置。

Picture of new constant eval error

常量求值错误

接下来,一旦我们知道位置,我们需要解释哪里出错了。我们通过标记代码中帮助解释错误的兴趣点来做到这一点。最明显的开始标记的地方是错误发生的地方。这是错误的“内容”。

在这个例子中,你可以看到我们如何使用这些主要标签。有了它们,你的眼睛可以同时看到有问题的代码和一些关于问题的描述。由于这是首先看到的最重要的地方,我们给它们一个醒目的红色外观,并带有特征性的^^^下划线。你会注意到在示例中,这种组合让你能够快速发现错误并理解哪里出了问题。

Picture of new trait mismatch

与 trait 要求不匹配的错误

错误的来源不是唯一的兴趣点。通常还有其他兴趣点可以帮助描述“为什么”会发生错误。通过阅读这些次要标签,你可以更好地理解哪里出错了。这些标签以它们在你的代码中出现的相同顺序显示,再次确保你始终能够一目了然地了解你所在的位置。

在这个例子中,次要标签显示了来自 trait 的原始要求,因此你可以同时看到它,并自己比较要求和实现。

主要标签和次要标签共同讲述一个关于哪里出错的故事。由于 Rust 非常关注借用检查器和内存安全,用户在遇到这些错误之一时可能会看到不熟悉的概念。这些标签有助于引导他们了解即使是不熟悉的错误(如借用错误)是如何发生的。

Picture of new type name not found

名称不在作用域内,带有建议

有时有太多信息无法放入标签中,因此新格式还支持附加额外的注释。就像之前的错误格式一样,新格式支持警告、建议、提示等。

扩展错误消息

在我们更新了错误消息格式后,我们寻找其他可以应用我们所学到的经验教训的领域。这里明显的赢家是--explain消息。顾名思义,--explain功能允许开发人员通过更长、更详细的解释来探索不熟悉的错误消息。

今天,当你调用--explain时,你需要传递一个错误代码。然后编译器会打印出一条扩展消息,其中详细介绍了该形式的错误是如何发生的

$ rustc --explain E0200

Unsafe traits must have unsafe implementations. This error occurs when an
implementation for an unsafe trait isn't marked as unsafe. This may be resolved
by marking the unsafe implementation as unsafe.

struct Foo;

unsafe trait Bar { }

// this won't compile because Bar is unsafe and impl isn't unsafe
impl Bar for Foo { }
// this will compile
unsafe impl Bar for Foo { }

这是帮助弥合错误和学习 Rust 中不熟悉概念之间差距的好方法。

虽然此消息很有帮助,但它使用的是一个可能与你的代码无关的通用示例。借鉴错误消息工作中的经验,我们将更新 explain 消息,使其专注于你的代码。例如,以我们开始使用的借用检查器错误为例,我们可能会有一条如下所示的扩展错误消息

error[E0499]: cannot borrow `foo.bar1` as mutable more than once at a time
  --> src/test/compile-fail/borrowck/borrowck-borrow-from-owned-ptr.rs:29:22

The borrow checker detected that `foo.bar1` was borrowed multiple
times as a mutable value. In Rust, this can not be done safely because
there may be multiple owners of the value who may write to it at the
same time. If this happens in parallel, the resulting value may be in
an unknown state.

Because this is unsafe, Rust disallows having multiple owners of the
same mutable value.

This is the first time `foo.bar1` is borrowed mutably.

28 |     let bar1 = &mut foo.bar1;
   |                     --------

And this is the second time `foo.bar1` is borrowed mutably. This is
where the error occurs.

29 |     let _bar2 = &mut foo.bar1;
   |                      ^^^^^^^^

Note that the first borrow of `foo.bar1` continues until the borrow
is released. During this time, no additional borrows can occur. This
first borrow ends here:

31 | }
   | -

After the first borrow has ended you are able to borrow it again. To
fix this issue, if you need to borrow a value as mutable more than
once, ensure that the span of time they are borrowed do not overlap.

在上面,你可以看到这种新的模板样式的可能输出。那些熟悉 Elm 风格的人可能会认识到,更新后的--explain消息从 Elm 的方法中汲取了大量灵感。

目前,这种格式仍在设计和开发中。如果你想帮助我们塑造扩展错误的模样,请加入 irc.mozilla.org 上的 #rust-cli 频道。

我想帮忙!

太好了!我们喜欢这种热情。有很多事情要做,以及很多技能可以在不同的方面帮助我们。无论你擅长单元测试、编写文档、编写代码还是设计工作,都有可以加入的地方。

结论

改进 Rust 是一项正在进行的活动。鉴于解决 Rust 学习曲线的重要性是 Rust 调查中的一个关键主题,我们比以往任何时候都更有动力去寻找 Rust 体验中任何令人困惑或分散注意力的部分,并对其进行充分的润色。错误是我们应用润色以帮助我们逐步改进学习曲线的一个方面,我们期待着看到我们能走多远。