新增的#[diagnostic]属性

发布时间:2024-06-20 22:12
最后更新:2024-06-21 10:12
所属分类:
Rust

随着Rust 1.78版本的发布,有很多还没有升级Rust编译器的小伙伴在升级依赖库的时候,可能就会遇到#[diagnostic]属性不支持的错误了。那么这个新增的#[diagnostic]到底有什么用途呢?

Diagnostic属性其实并不是Rust 1.78版本新增的功能,而是在1.78版本中稳定了下来,彻底放开使用了。这个属性是为了提供自定义编译器的错误输出内容设计的,可以允许通过属性,让错误发生时,编译器可以提供更加详细、清晰的错误信息。在目前的版本中,Diagnostic属性只有一个:#[diagnostic::on_unimplemented],目的是用来提示目前所使用的特征尚不存在针对指定类型的实现的错误。

以前的编译器输出

在引入Diagnostic属性之前的编译器输出,都是编译器内提前放置好的内容,比较缺乏说明性,虽然能够非常明确的指出问题,但是输出的内容却不够友善。例如以下这个示例。

1
2
3
4
5
6
7
trait ImplementMe<T> {}

fn use_trait<T: ImplementMe<T>>(_: T) {}

fn main() {
    use_trait(String::new());
}

在这个示例中,对于特征ImplementMe<T>,没有提供其在String类型上的实现,所以在编译的时候会报出以下错误。

未提供实现时编译器的默认报错信息
未提供实现时编译器的默认报错信息

在编译器的原生提示中,对于未提供实现的错误给出的提示是对于指定特征的绑定未被满足。如果是一直在做Rust开发的人,可能对这种错误提示已经很熟悉了,但还是要说这个提示的确不是那么“平易近人”。

用Diagnostic属性自定义错误输出

那么在引入了Diagnostic属性以后,上面这个示例中的代码就可以通过增加#[diagnostic::on_unimplemented()]属性标记来变成以下这个样子。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#[diagnostic::on_unimplemented(
    message = "这个特征对于类型 {Self} 还没有提供 ImplementMe<{T}> 的实现",
    label = "ImplementMe<{T}>没有实现",
    note = "请为类型{Self}实现ImplementMe<{T}>"
)]
trait ImplementMe<T> {}

fn use_trait<T: ImplementMe<T>>(_: T) {}

fn main() {
    use_trait(String::new());
}

现在我们再来编译一下看看,此时编译器提供的错误提示信息变成了什么样子。

使用Diagnostic属性增强的报错信息
使用Diagnostic属性增强的报错信息

#[diagnostic::on_unimplmented()]属性可以接受三个参数:messagelabelnote,其中note参数可以有多个。这几个参数具体输出的信息可以看上面的示例与截图中的信息对照来确定它们出现的位置。

如果提供了多个note,那么每个note将会占据一行的输出。

#[diagnostic:on_unimplemented]属性中,提供了一些用来替代功能类型的模板标记。例如{Self}指代当前没有提供实现的目标类型,{T}则是直接引用泛型定义中的特定类型参数。其实与Rust代码中的关键字和常用内容对比一下就知道应该如何使用了。

低版本Rust如何兼容

如果正在使用的Rust Toolchain是1.78以前的版本,那么在编译使用了Diagnostic属性的依赖库的时候,就会出现 `#[diagnostic]` attribute name space is experimental的错误提示,而且编译还会失败。

此时如果不想升级Rust Toolchain,那么可以在main.rs文件顶部增加一行#![feature(diagnostic)],即可在1.78版本之前的Rust Toolchain中启动这项实验性功能。

如果这样做了,你的Rust编译器依旧报错,那么你用的Rust版本实在太老了,忍痛升个级吧。

索引标签
Rust
属性
diagnostic