你好!
在过去的几天里,我一直在用 Rust 做Advent of Code ,因为我从来没有真正适应过这门语言,我认为做一些 Advent of Code 的问题可能会有帮助。
我的解决方案没有什么特别的,但是因为我正在努力学习,所以我一直在尝试对编译器错误采取比通常更严格的方法。我不只是修复错误并继续前进,而是试图确保我真正理解错误消息的含义以及它告诉我的关于语言如何工作的内容。
我的步骤是:
- 修复错误
- 制作一个小型独立程序,重现相同的编译器错误
- 考虑一下并尝试向自己解释,以确保我真正理解为什么会发生该错误
- 如果我仍然不明白,请寻求帮助
所以这里有几个编译器错误以及我对自己为什么会发生错误的解释。
它们都是非常基本的 Rust 错误,但我今天想到它们很开心。我写这篇文章是为了想象中的“那些了解一些 Rust 基础知识但仍然不擅长 Rust 的人”,如果你们中有人的话。
错误一:借用错误
这是一些代码( rust playground 链接):
fn inputs() -> Vec<(i32, i32)> { return vec![(0, 0)]; } fn main() { let scores = inputs().iter().map(|(a, b)| { a + b }); println!("{}", scores.sum::<i32>()); }
这是编译器错误:
5 | let scores = inputs().iter().map(|(a, b)| { | ^^^^^^^^ creates a temporary which is freed while still in use 6 | a + b 7 | }); | - temporary value is freed at the end of this statement 8 | println!("{}", scores.sum::<i32>()); | ------ borrow later used here help: consider using a `let` binding to create a longer lived value | 5 ~ let binding = inputs(); 6 ~ let scores = binding.iter().map(|(a, b)| { | For more information about this error, try `rustc --explain E0716`.
这是一个关于借用的非常基本的 Rust 错误消息,但我已经忘记了关于 Rust 的所有内容,所以我不明白。
有两件事我不知道/没有想到这里:
第 1 件事:变量在 Rust 中具有语义意义。
我的意思是这段代码:
let scores = inputs().iter().map(|(a, b)| { ... };
在这段代码中,我们将inputs()
分解为一个变量并没有做同样的事情:
let input = inputs(); let scores = input.iter().map(|(a, b)| { ... };
如果分配了一些内存并且它不在自己的变量中,那么它会在表达式的末尾被释放(尽管这显然有一些例外,请参阅rustc --explain E0716
了解更多信息)。但它确实有自己的变量,然后它一直保留到块结束。
在错误消息中,Rust 编译器实际上建议阅读解释 ( rustc --explain E0716
),它解释了所有这些以及更多内容。我没有立即注意到它,但是一旦我阅读了它(并用 Google 搜索了一下),它确实对我有帮助。
事情2 :。使用iter()
的计算不会立即发生。
这是我理论上知道的事情,但没有考虑它与编译器错误的关系。
当我调用.map(...)
时,实际上并没有立即执行map
——它只是设置了一个迭代器,当我们调用.sum()
时,它可以稍后进行实际计算。
这意味着我需要保留来自inputs()
的内存,因为还没有任何计算发生!
错误 2:求和Iterator<()>
这是一些代码( rust playground 链接)(这不是我正在调试的实际代码,但它是演示错误消息的最快方式)
fn main() { vec![(), ()].iter().sum::<i32>(); }
这有一个非常明显的错误:您不能对一堆()
(空类型)求和并得到i32
作为结果。不过,这是编译器错误:
2 | vec![(), ()].iter().sum::<i32>(); | ^^^^^^^^^^^^^^^^^^^ --- required by a bound introduced by this call | | | the trait `Sum<&()>` is not implemented for `i32`
这让我很困惑——我希望看到一个错误,说类似Sum is not implemented for Iterator<()>
。但相反,它说Sum
没有为i32实现。但我不是想总结 i32s!这是怎么回事?
这里实际发生的事情是(感谢一些帮助我的可爱的人!):
-
i32
有一个名为sum(iter: Iterator<i32>)
的静态方法,它来自Sum
特征。 ( 此处为整数定义) -
Iterator
有一个sum()
方法,它在i32
上调用这个静态方法( 定义在这里) - 当我运行
my_iter.sum()
时,它会尝试调用i32::sum(my_iter)
- 但是
i32::sum
没有为Iterator<&()>
定义! -
Sum
中的类型参数(例如)Sum<&()>
指的是传递给i32::sum()
的迭代器的类型 - 结果,我们得到错误消息
the trait Sum<&()> is not implemented for i32
我可能没有完全正确地理解所有类型/术语,但我认为这就是它的要点。
这是一个很好的提醒,有时方法(如Iterator
上的sum()
以稍微间接/违反直觉的方式定义,您必须追查其实现方式的细节以理解编译器错误。
(我这里的实际错误实际上是我不小心添加了一个额外的;
在我的代码中,这意味着我不小心创建了一个Iterator<()>
而不是Iterator<i32>
,并且令人困惑的错误消息使它更难理解出那个出)
Rust 错误消息很酷
我发现这些错误消息非常有用,我特别喜欢关于借用错误的--explain
输出。
原文: https://jvns.ca/blog/2022/12/02/a-couple-of-rust-error-messages/