Rust 团队很高兴宣布 Rust 的新版本 1.30.0。Rust 是一种系统编程语言,专注于安全、速度和并发。
如果您之前已通过 rustup 安装了 Rust,获取 Rust 1.30.0 非常简单:
$ rustup update stable
如果您还没有,您可以从我们网站上的相应页面获取 rustup
,并查看 GitHub 上的1.30.0 的详细发布说明。
1.30.0 稳定版中的内容
Rust 1.30 是一个令人兴奋的版本,包含许多新功能。在周一,预计会有另一篇博客文章要求您查看 Rust 1.31 的 beta 版本;Rust 1.31 将是“Rust 2018”的第一个版本。有关该概念的更多信息,请参阅我们之前发布的博客文章“什么是 Rust 2018”。
过程宏
早在Rust 1.15中,我们就宣布了定义“自定义派生”的功能。例如,使用 serde_derive
,您可以
#[derive(Serialize, Deserialize, Debug)]
struct Pet {
name: String,
}
并使用 serde_json
将 Pet
转换为 JSON 格式,因为 serde_derive
在过程宏中定义了 Serialize
和 Deserialize
。
Rust 1.30 通过添加定义另外两种高级宏的能力来扩展此功能,即“属性式过程宏”和“函数式过程宏”。
属性式宏类似于自定义派生宏,但它们不仅为 #[derive]
属性生成代码,还允许您创建自己的新自定义属性。它们也更灵活:派生仅适用于结构体和枚举,而属性可以放在其他地方,例如函数。作为使用属性式宏的示例,您可能在使用 Web 应用程序框架时会遇到类似以下情况
#[route(GET, "/")]
fn index() {
此 #[route]
属性将由框架本身定义,作为过程宏。它的签名将如下所示
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
这里,我们有两个输入 TokenStreams
:第一个用于属性本身的内容,即 GET, "/"
部分。第二个是属性所附加内容的主体,在本例中是 fn index() {}
和函数主体的其余部分。
函数式宏定义看起来像函数调用的宏。例如,gnome-class
crate 具有一个过程宏,它在 Rust 中定义了 GObject
类
gobject_gen!(
class MyClass: GObject {
foo: Cell<i32>,
bar: RefCell<String>,
}
impl MyClass {
virtual fn my_virtual_method(&self, x: i32) {
// ... do something with x ...
}
}
)
这看起来像一个函数,它将大量代码作为参数。此宏将这样定义
#[proc_macro]
pub fn gobject_gen(input: TokenStream) -> TokenStream {
这类似于派生宏的签名:我们获取括号内的标记,并返回我们要生成的代码。
use
和宏
您现在可以使用 use
关键字将宏引入作用域。例如,要使用 serde-json
的 json
宏,您以前需要编写
#[macro_use]
extern crate serde_json;
let john = json!({
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
});
但现在,您需要编写
extern crate serde_json;
use serde_json::json;
let john = json!({
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
});
这使宏更符合其他项目,并消除了对 macro_use
注释的需求。
最后,proc_macro
crate 已稳定,它为您提供了编写这些宏所需的 API。它还显著改进了错误 API,syn
和 quote
等 crate 已经在使用它们。例如,以前
#[derive(Serialize)]
struct Demo {
ok: String,
bad: std::thread::Thread,
}
会给出以下错误
error[E0277]: the trait bound `std::thread::Thread: _IMPL_SERIALIZE_FOR_Demo::_serde::Serialize` is not satisfied
--> src/main.rs:3:10
|
3 | #[derive(Serialize)]
| ^^^^^^^^^ the trait `_IMPL_SERIALIZE_FOR_Demo::_serde::Serialize` is not implemented for `std::thread::Thread`
现在它将给出以下错误
error[E0277]: the trait bound `std::thread::Thread: serde::Serialize` is not satisfied
--> src/main.rs:7:5
|
7 | bad: std::thread::Thread,
| ^^^ the trait `serde::Serialize` is not implemented for `std::thread::Thread`
模块系统改进
模块系统长期以来一直是新 Rustaceans 的痛点;它的许多规则在实践中感觉很别扭。这些更改是我们为使模块系统感觉更直接而采取的第一步。
除了前面提到的宏更改之外,use
还进行了两个更改。第一个是外部 crate 现在位于 prelude 中,即
// old
let json = ::serde_json::from_str("...");
// new
let json = serde_json::from_str("...");
这里的技巧是,由于 Rust 的模块系统的工作方式,“旧”样式并不总是需要的
extern crate serde_json;
fn main() {
// this works just fine; we're in the crate root, so `serde_json` is in
// scope here
let json = serde_json::from_str("...");
}
mod foo {
fn bar() {
// this doesn't work; we're inside the `foo` namespace, and `serde_json`
// isn't declared there
let json = serde_json::from_str("...");
}
// one option is to `use` it inside the module
use serde_json;
fn baz() {
// the other option is to use `::serde_json`, so we're using an absolute path
// rather than a relative one
let json = ::serde_json::from_str("...");
}
}
将函数移动到子模块并导致部分代码中断并不是一种很好的体验。现在,它将检查路径的第一部分,看看它是否是一个 extern crate
,如果是,则无论您在模块层次结构中的哪个位置,都将使用它。
最后,use
还支持使用以 crate
开头的路径将项目引入作用域
mod foo {
pub fn bar() {
// ...
}
}
// old
use ::foo::bar;
// or
use foo::bar;
// new
use crate::foo::bar;
路径开头处的 crate
关键字表示您希望路径从您的 crate 根目录开始。以前,在 use
后指定的路径始终从 crate 根目录开始,但引用直接项目的路径将从本地路径开始,这意味着路径的行为不一致
mod foo {
pub fn bar() {
// ...
}
}
mod baz {
pub fn qux() {
// old
::foo::bar();
// does not work, which is different than with `use`:
// foo::bar();
// new
crate::foo::bar();
}
}
一旦这种样式得到广泛使用,这将有望使绝对路径更加清晰,并消除一些前导 ::
的丑陋之处。
所有这些更改相结合,使人们对路径解析方式的理解更加直观。无论您在 use
语句以外的任何地方看到 a::b::c
之类的路径,您都可以问
a
是一个 crate 的名称吗?那么我们在它里面寻找b::c
。a
是关键字crate
吗?那么我们在 crate 的根目录中寻找b::c
。- 否则,我们在模块层次结构中的当前位置寻找
a::b::c
。
use
路径始终从 crate 根目录开始的旧行为仍然适用。但是,在一次性切换到新样式后,这些规则将统一应用于所有地方的路径,并且在移动代码时,您需要调整导入的次数将大大减少。
原始标识符
您现在可以使用关键字作为标识符,使用一些新的语法
// define a local variable named `for`
let r#for = true;
// define a function named `for`
fn r#for() {
// ...
}
// call that function
r#for();
这在今天没有太多用例,但当您尝试将 Rust 2015 crate 与 Rust 2018 项目(反之亦然)一起使用时,它将派上用场,因为两个版本中的关键字集将不同;我们将在即将发布的有关 Rust 2018 的博客文章中详细解释。
no_std
应用程序
早在 Rust 1.6 中,我们就宣布了no_std
和 libcore
的稳定化,用于构建没有标准库的项目。不过,有一个问题:您只能构建库,而不能构建应用程序。
使用 Rust 1.30,您可以使用 #[panic_handler]
属性自行实现恐慌。这意味着您现在可以构建不使用标准库的应用程序,而不仅仅是库。
其他内容
最后,您现在可以使用在宏中匹配可见性关键字(如 pub
),使用 vis
说明符。此外,#[rustfmt::skip]
之类的“工具属性”现在已稳定。但是,#[allow(clippy::something)]
之类的工具lint 尚未稳定。
有关更多信息,请参阅详细的发布说明。
库稳定化
此版本稳定了一些新的 API
Ipv4Addr::{BROADCAST, LOCALHOST, UNSPECIFIED}
Ipv6Addr::{LOCALHOST, UNSPECIFIED}
Iterator::find_map
此外,标准库长期以来一直具有 trim_left
之类的函数,用于消除某些文本一侧的空格。但是,在考虑从右到左 (RTL) 语言时,“右”和“左”的含义会变得令人困惑。因此,我们正在为这些 API 引入新的名称
trim_left
->trim_start
trim_right
->trim_end
trim_left_matches
->trim_start_matches
trim_right_matches
->trim_end_matches
我们计划在 Rust 1.33 中弃用(但当然不会删除)旧名称。
有关更多信息,请参阅详细的发布说明。
Cargo 功能
此版本中 Cargo 的最大功能是,我们现在有了进度条!
有关更多信息,请参阅详细的发布说明。
1.30.0 的贡献者
许多人共同创建了 Rust 1.30。没有你们,我们不可能做到。感谢!