外刊评论|Rust 2021 有哪些新功能

1,384 阅读5分钟

编辑:张汉东/ 王江桐(华为)

编者按:国外的 Rust 资源非常丰富,将其都翻译过来也不现实,所以想到这样一个栏目。外刊评论,主要是收集优秀的 Rust 相关文章,取其精华,浓缩为一篇简单评论。

欢迎大家贡献:github.com/RustMagazin…

通常情况下来说,Rust语言组尽量让所有新增的功能在未来的版本都能适用。然而,在一些情况下,新增的功能会使之前的代码失效。例如,新版本中或许会使用新的关键词,比如await,如果之前的代码里有名为await的变量,那么变量就会因为关键词的原因而失效,造成错误。在这种情况下,Rust会发布一个新版本,并用版本(edition)来使编译器知晓代码将使用哪个版本的Rust。

在Rust 2018之后,Rust的新版本Rust 2021(1.56.0)目前暂定于9月9号进行beta 测试,10月21号正式上线。

Rust 2021 有哪些新功能

来自《This Week in Rust 》第404篇博文

对于std::preclude, 增加了新的引用

std::preclude是每一个rs module默认引入的库,不需要程序员自己手动引用。当与用户自定义的库有冲突时,会优先使用用户引入的库来保证代码没有二义性。当用户自定义try_into方法时,如果同时引用标准库的try_into,并且希望同一个rs文件中,两个不同的对象一个使用用户自定义的try_into,一个使用标准库的try_into,此时由于try_into具有二义性,编译器会报错。为了解决这个问题,Rust 2021决定将新的trait加入std::preclude.

新版本的preclude与老版本相比,没有删除任何项,只是新增了三个trait: std::convert::TryInto, std::convert::TryFrom, std::iter::FromIterator。

Cargo Feature Resolver

自从1.51.0,Rust引入了新的resolver(resolver = "2"),来解决功能对于编译环境与发布环境不同时兼容的问题。

例如,对于依赖foo与其功能A和B,假设有两个包,bar依赖于foo + A,和baz依赖于foo + B。当resolver = “1”时,Cargo在编译的时候会合并功能A和B,并且编译foo + AB。这样做的好处是,foo只会被编译一次;但是问题在于,如果foo + A仅作为dev dependencies使用、foo + B仅作为target dependencies使用、且开发环境和目标环境并不同时兼容A和B呢?foo + AB将无法同时在两个平台上编译。

resolver = "2"可以解决这个问题,因为它将单独编译并使用这些功能。比如在这个例子中,foo + A只会在当前build包含dev dependencies时被启用,模块将会被分开单独编译,而不会一起编译。这确实或许会增加编译时间,但是同时可以解决在不同平台与环境上的编译问题。

对于1.56(Rust 2021)之前的版本,虽然或许resolver = "2"已被引入,但是默认的resolver仍然是”1“。Rust 2021以及后续版本将把默认resolver设定为”2“。如果项目确实有以上顾虑,请不要忘记修改Cargo.toml。

闭包与所有权

闭包提供匿名函数功能,并且闭包中可以捕获调用者作用域中的值。在之前版本中,对于结构体a,

struct a{
	x: int,
	y: int,
}

如果有如下闭包:

|| a + 1;

闭包将会获取对于a的不可变引用。

如果有如下闭包:

|| a.x + 1;

闭包将会获取对a(整个a)的不可变引用,而不是单独对于a.x的。如果a的其他属性(e.g. a.y)被删除,那么这个闭包将无法编译,因为闭包要求获取对于整个a的引用,而a的部分属性已经失效。

let a = SomeStruct::new();

drop(a.x); // Move out of one field of the struct

println!("{}", a.y); // Ok: Still use another field of the struct

let c = || println!("{}", a.y); // Error: Tries to capture all of `a`
c();

然而对于Rust 2021,闭包将不会再获取对于整个a的引用。对于a.x + 1,闭包将只获取a.x这个属性的引用。

#### 语法与前缀

其他一些有关于语法,格式,前缀的修改:

  • panic!将允许单一参数,并将其识别为format string;

  • 列表循环支持类python写法:for e in [1, 2, 3];

  • 为未来的语法和新功能保留一些前缀:prefix#identifier, prefix"string", prefix'c', and prefix#123,其中prefix可能是任何标识符;

    • 未来计划会有的新前缀:类同python写法的format string: f"",c""或者z""表示C类型的String,etc.
  • Warning升格至Error;

  • macro_rules的Or(|)的使用;

更详细的说明请参阅:blog.rust-lang.org/2021/05/11/…

如何升级到Rust新版本

在Cargo.toml中我们可以手动配置使用的Rust版本:

[package]
edition = "2021"

同时可以利用Cargo帮助我们自动修订对于版本不适用的代码。cargo fix --edition (--all features --target (platform, x86, lib/bin, etc.))可以帮助修复cargo check所找出的所有在local package以及这些package的dependencies之中的错误。

例如dyn,对于一些语法关键词,可以通过cargo fix --edition-idioms进行自动修改。

如果不怕代码报错的话,可以使用cargo fix --edition-idioms --broken-code,让Cargo修改所有它觉得有问题的地方。这可能导致代码无法编译。

引用