Rust 团队很高兴地宣布 Rust 新版本 1.45.0 发布。Rust 是一种旨在帮助每个人构建可靠且高效软件的编程语言。
如果您之前通过 rustup 安装了 Rust,那么升级到 Rust 1.45.0 非常简单,只需执行:
$ rustup update stable
如果您还没有安装 rustup,您可以从我们网站的相应页面获取 rustup
,并查看 GitHub 上 1.45.0 详细版本发布说明。
1.45.0 稳定版的新特性
Rust 1.45.0 有两个重要的变更需要注意:修复了整数和浮点数之间转换时长期存在的 unsoundness 问题,以及稳定了使一个流行的 Web 框架能够在稳定版 Rust 上运行所需的最后一个特性。
修复类型转换中的 unsoundness 问题
Issue 10184 最初在 2013 年 10 月被提出,那时距离 Rust 1.0 发布还有一年半的时间。您可能知道,rustc
使用 LLVM 作为编译器后端。当您编写如下代码时:
pub fn cast(x: f32) -> u8 {
x as u8
}
Rust 1.44.0 及更早版本的 Rust 编译器会生成如下所示的 LLVM-IR:
define i8 @_ZN10playground4cast17h1bdf307357423fcfE(float %x) unnamed_addr #0 {
start:
%0 = fptoui float %x to i8
ret i8 %0
}
fptoui
实现了类型转换,它是 "floating point to unsigned integer"(浮点数到无符号整数)的缩写。
但这里有一个问题。来自 文档:
‘fptoui’ 指令将其浮点操作数转换为最接近的(向零舍入)无符号整数值。如果该值无法容纳在 ty2 中,则结果是一个 poison value。
现在,除非您经常深入研究编译器,否则您可能不理解这意味着什么。它充满了术语,但有一个更简单的解释:如果您将一个很大的浮点数转换为一个很小的整数,您会得到未定义行为。
这意味着,例如,以下代码没有明确定义:
fn cast(x: f32) -> u8 {
x as u8
}
fn main() {
let f = 300.0;
let x = cast(f);
println!("x: {}", x);
}
在 Rust 1.44.0 上,这段代码在我的机器上恰好打印 "x: 0"。但它可能打印任何内容,或执行任何操作:这是未定义行为。但是,unsafe
关键字没有在这段代码块中使用。这就是我们所说的 "soundness" bug,也就是说,这是一个编译器行为错误的问题。我们在 issue 跟踪器上将这些 bug 标记为 I-unsound,并非常认真地对待它们。
但是,这个 bug 花费了很长时间才解决。原因是正确的前进方向非常不明确。
最终,决定这样做:
as
将执行“饱和转换”。- 如果您想跳过检查,将会添加一个新的
unsafe
转换。
这与数组访问非常相似,例如:
array[i]
将检查以确保array
至少有i + 1
个元素。- 您可以使用
unsafe { array.get_unchecked(i) }
来跳过检查。
那么,什么是饱和转换呢?让我们看一个稍微修改过的例子:
fn cast(x: f32) -> u8 {
x as u8
}
fn main() {
let too_big = 300.0;
let too_small = -100.0;
let nan = f32::NAN;
println!("too_big_casted = {}", cast(too_big));
println!("too_small_casted = {}", cast(too_small));
println!("not_a_number_casted = {}", cast(nan));
}
这将打印:
too_big_casted = 255
too_small_casted = 0
not_a_number_casted = 0
也就是说,太大的数字会变成最大可能的值。太小的数字会产生最小可能的值(即零)。NaN 产生零。
以不安全方式进行转换的新 API 是:
let x: f32 = 1.0;
let y: u8 = unsafe { x.to_int_unchecked() };
但与往常一样,您应该仅在万不得已的情况下才使用此方法。就像数组访问一样,编译器通常可以优化掉检查,使得在编译器可以证明安全的情况下,安全版本和不安全版本是等效的。
稳定化类函数过程宏,使其可在表达式、模式和语句中使用
在 Rust 1.30.0 中,我们稳定了 “item 位置的类函数过程宏”。例如,gnome-class
crate:
Gnome-class 是 Rust 的一个过程宏。在宏内部,我们定义了一种尽可能像 Rust 的迷你语言,并且具有扩展功能,可让您定义 GObject 子类、它们的属性、信号、接口实现以及 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 ...
}
}
}
“item 位置” 是一些术语,但基本上这意味着您只能在代码的某些位置调用 gobject_gen!
。
Rust 1.45.0 增加了在三个新位置调用过程宏的能力:
// imagine we have a procedural macro named "mac"
mac!(); // item position, this was what was stable before
// but these three are new:
fn main() {
let expr = mac!(); // expression position
match expr {
mac!() => {} // pattern position
}
mac!(); // statement position
}
能够在更多位置使用宏是很有趣的,但还有另一个原因让许多 Rust 爱好者长期以来一直在等待此功能:Rocket。Rocket 最初于 2016 年 12 月发布,是一个流行的 Rust Web 框架,通常被描述为 Rust 生态系统提供的最佳工具之一。这是其即将发布的版本中的 “hello world” 示例:
#[macro_use] extern crate rocket;
#[get("/<name>/<age>")]
fn hello(name: String, age: u8) -> String {
format!("Hello, {} year old named {}!", age, name)
}
#[launch]
fn rocket() -> rocket::Rocket {
rocket::ignite().mount("/hello", routes![hello])
}
直到今天,Rocket 仍然依赖于 nightly-only 功能来实现其灵活性和人体工程学的承诺。实际上,正如在 项目主页 上看到的那样,当前版本的 Rocket 中的相同示例需要 proc_macro_hygiene
功能才能编译。但是,正如您可能从该功能的名称中猜到的那样,今天它在稳定版中发布了!这个 issue 跟踪了 Rocket 中 nightly-only 功能的历史。现在,它们都已完成!
Rocket 的下一个版本仍在开发中,但发布后,许多人会非常高兴 :)
库变更
在 Rust 1.45.0 中,以下 API 被稳定化:
Arc::as_ptr
BTreeMap::remove_entry
Rc::as_ptr
rc::Weak::as_ptr
rc::Weak::from_raw
rc::Weak::into_raw
str::strip_prefix
str::strip_suffix
sync::Weak::as_ptr
sync::Weak::from_raw
sync::Weak::into_raw
char::UNICODE_VERSION
Span::resolved_at
Span::located_at
Span::mixed_site
unix::process::CommandExt::arg0
此外,您可以将 char
与 ranges 结合使用,以迭代码位:
for ch in 'a'..='z' {
print!("{}", ch);
}
println!();
// Prints "abcdefghijklmnopqrstuvwxyz"
有关更改的完整列表,请参阅完整的版本发布说明。
其他变更
Rust 1.45.0 版本还有其他更改:请查看 Rust、Cargo 和 Clippy 中的更改。
1.45.0 版本贡献者
许多人齐心协力创建了 Rust 1.45.0。没有你们所有人,我们不可能做到。感谢!