在上期博客 中,我们根据 RustRover 的使用数据深入分析了 10 大最常见 Rust 编译器错误中的后 5 个。在本部分中,我们将探讨最常发生的前 5 大错误,并对 Rust 编程语言中最容易使开发者与编译器产生“冲突” 的方面进行一些更总体的观察。
常见错误 #5:E0433(使用了未声明的 crate、模块或类型 )
这个错误类似于上一部分中谈到的 E0432(import 未解析)。唯一的区别是,具有问题组件的路径直接在名称中使用,不带 use 子句。17.5% 的 RustRover 用户遇到过这个错误。例如,在下面的代码中,编译器无法理解我们真正的意图(crate 还是模块)并报错:
这个示例展示了 Rust 编译器备受好评的错误消息传递。即使不阅读消息,修正也显而易见:验证名称,根据需要将所需的依赖项添加到Cargo.toml ,或者引入适当的 import。
RustRover 也可以发挥作用, 让我们选择导入的名称。
编写代码时使用代码补全就可以完全避免这种错误。
常见错误 #4:E0425(使用了未解析的名称 )
接下来是另一个“未解析”的错误。20.5% 的 RustRover 用户遇到过这种情况:使用了未解析的名称,它在语法上不是 crate 或模块名称。发生这种错误时会弹出:
当然,实际发生时,情况可能不会像上图中那样明朗。
可惜,即使是了不起的 Rust 编译器也难以应对这种错误。可以采取的方式是:修订名称、提供定义或引入适当的 import。
常见错误 #3:E0599(方法用于未实现该方法的类型 )
剧透预警:从现在起都将是类型检查错误!没错,前三名的错误都与不正确的类型使用相关。这里的错误是,27.5% 的 RustRover 用户曾试图在未实现方法的类型的值调用上该方法。来看下面的例子:
fn main ( ) { let numbers: Vec = vec![4 , 11 , 15 , 12 ]; let sum: usize = numbers.sum(); println!("Answer = {sum}" ); }
看起来没问题,只是没有作用于 Vec 的 sum 方法。具体错误消息如下:
error [E0599]: `Vec` is not an iterator
这里的修正是首先调用 iter 方法,然后在生成的迭代器上使用 sum。
另外,RustRover 有一项功能可以防止这种错误, 即链式补全 。要查看实际益处,请查看以下示例:
当补全建议被采纳时,iter 和 sum 方法都会添加到调用链中,立即修正代码。
通常,代码补全可以帮助您避免 E0599 消息。如果您没有看到所需方法调用的建议,那么真正的问题可能是被调用方类型不正确。
这个错误还有一个方面值得一提:在许多上下文中,您可以控制自己的类型声明。一种选项是首先调用一个方法,然后按 Alt-Enter (⌥Option –Return ) 并执行建议的操作让 RustRover 搭建其实现:
操作的结果是,RustRover 生成方法实现模板,同时考虑到有关类型的所有上下文信息:
impl Info { pub(crate) fn summary(&self ) -> String { todo!() } }
运行这段代码会导致运行时错误,但至少编译器不再报错。
常见错误 #2:E0308(预期类型与接收的类型不匹配 )
设想一下:在需要类型 B 的值的上下文中使用了类型 A 的表达式。这里的“上下文”可以指调用点处的函数形参、变量声明、控制流语句/表达式等。如果您遇到过这个问题,那么您也是 30% 的 RustRover 用户中的一员。
理论上,对于这种情况我们有两种选择:调整值或调整上下文。调整值可能涉及转换类型、引用/解引用、在值上调用转换方法或将其替换为其他内容。调整上下文并不总是可行,有时我们可以更改预期类型来匹配接收的类型。
RustRover 通过提供快速修复选项支持两种操作模式,例如:
注意列表中的第二个建议:在这种情况下,RustRover 提出了适合编译器的相当复杂的值转换。遵循第一个建议将修正上下文,即函数定义。
常见错误 #1:E0277(尝试使用一个类型,但该类型未在需要特征的位置实现特征 )
我们终于到榜首了!RustRover 中最常见的 Rust 编译器错误是 E0277, 32% 的 RustRover 用户经历过这种以类型和特征为主题的开发者噩梦。像往常一样,官方说明 很好地提供了示例并解释了可能的修正。我们来关注 RustRover 的行为。
对于这种错误,我最喜欢的 RustRover 功能如下所示:
编译器报错:
error[E0277]: `Info` doesn't implement `std ::fmt::Display`
当然,实现 Display 特征是其中一种选择,RustRover 也很乐意搭建实现。但在这种情况下,我更倾向于采用第一个建议,其中涉及两个同时进行的步骤:
然而,在许多其他情况下,RustRover 无法自行发现和高亮显示此错误。如前一部分所述,它的类型检查功能还没有那么强大,但我们正在改进。作为最常见的错误消息,E0277 当然在我们的关注范围之内。
Rust 错误的总体观察
我们来看概况。上周,我们向 X(以前称为 Twitter)社区询问 Rust 编程语言的什么方面是代码错误的主要来源。以下是我们得到的回复:
为了将社区回复与我们从数据中观察到的结果进行比较,我们研究了 25 个最常见的 Rust 编译器错误,并将其大致分为五类:
类型和特征
所有权和生命周期
宏
未解析的名称或不存在的元素
其他
结果如下(按错误代码数字从小到大的顺序排列):
错误代码
描述
类别
E0061
调用函数时传递的实参数量无效
其他
E0063
未提供结构或类结构枚举变体的字段。
未解析/不存在
E0106
此错误表明类型缺少生命周期。
所有权和生命周期
E0107
提供的泛型实参数量不正确。
类型和特征
E0277
尝试使用一个类型,但该类型未在需要特征的位置实现特征
类型和特征
E0282
编译器无法推断类型并要求类型注解。
类型和特征
E0283
由于缺乏信息无法明确选择实现。
类型和特征
E0308
预期类型与接收的类型不匹配。
类型和特征
E0369
尝试对不支持的类型进行二元运算。
类型和特征
E0382
内容移动到其他位置后变量才被使用。
所有权和生命周期
E0412
使用的类型名称不在作用域内。
未解析/不存在
E0423
标识符像函数名称一样使用,或者需要一个值并且标识符存在,但它属于不同的命名空间。
未解析/不存在
E0425
使用了未解析的名称。
未解析/不存在
E0432
import 未解析。
未解析/不存在
E0433
使用了未声明的 crate、模块或类型。
未解析/不存在
E0502
已被借用为不可变对象的变量被借用为可变对象。
所有权和生命周期
E0507
借用的值被移出。
所有权和生命周期
E0515
返回了对局部变量的引用。
所有权和生命周期
E0596
发生此错误是因为您尝试以可变方式借用不可变变量。
所有权和生命周期
E0597
发生此错误是因为值在借用期间被删除。
所有权和生命周期
E0599
发生此错误是因为方法用于未实现该方法的类型。
类型和特征
E0609
尝试访问结构中不存在的字段。
未解析/不存在
E0614
尝试解引用无法解引用的变量。
其他
E0658
使用了不稳定的函数。
其他
E0716
临时值正被删除,而借用仍在有效使用中。
所有权和生命周期
很遗憾,我们掌握的数据并没有太多关于宏的信息。我们既无法可靠地检测宏展开问题,也无法识别源自成功展开的宏的其他错误。也许这表明我们应该对收集的数据进行更精细的分类。
不考虑宏,前 25 大常见错误中似乎没有明显的赢家,这说明我们平等地喜爱 Rust 的每一面:
类型和特征 – 7 个错误
所有权和生命周期 – 8 个错误
未解析的名称或不存在的元素 – 7 个错误
其他 – 3 个错误
概要
在本系列的这一部分中,我们研究了 RustRover 中的五大最常见 Rust 编译器错误。其中三种错误与类型和特征有关,表明这一错误类别对于正确编写 Rust 代码颇为关键。我们观察了 25 个最常见错误的总体数据,意识到 Rust 的其他方面也会导致我们经常遇到的错误。
本博文英文原作者:Vitaly Bragilevsky
RustRover 是 JetBrains 在 2023 年推出的面向 Rust 开发者的全新 IDE,正处于公开预览阶段,供大众免费使用体验。