Merge pull request #1135 from EluvK/eluvk/translation/appendix/rust-versions

translation(appendix/rust-versions) : 1.63 - 1.67
pull/1144/head
Sunface 2 years ago committed by GitHub
commit ba25e0eb31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -342,3 +342,8 @@
- [1.60](appendix/rust-versions/1.60.md) - [1.60](appendix/rust-versions/1.60.md)
- [1.61](appendix/rust-versions/1.61.md) - [1.61](appendix/rust-versions/1.61.md)
- [1.62](appendix/rust-versions/1.62.md) - [1.62](appendix/rust-versions/1.62.md)
- [1.63](appendix/rust-versions/1.63.md)
- [1.64](appendix/rust-versions/1.64.md)
- [1.65](appendix/rust-versions/1.65.md)
- [1.66](appendix/rust-versions/1.66.md)
- [1.67](appendix/rust-versions/1.67.md)

@ -0,0 +1,75 @@
# Rust 新版解读 | 1.63 | 重点: Socped threads
> Rust 1.63 官方 release doc: [Announcing Rust 1.63.0 | Rust Blog](https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html)
通过 [rustup](https://www.rust-lang.org/tools/install) 安装的同学可以使用以下命令升级到 1.63 版本:
```shell
$ rustup update stable
```
## 区域线程 Scoped threads
Rust 从 1.0 版本起,就可以使用 `std::thread::spawn` 来创建一个线程,但是这个函数要求了其生成的线程必须拥有任何传递进去的参数的所有权,也就是说你不能把引用数据传递进去。在一些线程会在方法末尾退出的情况下(通常使用 `join()` 方法),这个严格的约束显得不必要,在此之前也通常使用 `Arc` 包裹数据的的方法来妥协。
随着 1.63 版本的推出,标准库新增了**区域线程**,允许在区域 `scope` 内创建使用当前调用栈内引用数据的线程。`std::thread::scope` 的API保证其中创建的线程会在自身返回前推出也就允许安全的借用数据。看下面的例子`scope` 内创建两个线程来,分别借用了数据:
```rust
let mut a = vec![1, 2, 3];
let mut x = 0;
std::thread::scope(|s| {
s.spawn(|| {
println!("hello from the first scoped thread");
// 可以借用变量 `a`
dbg!(&a);
});
s.spawn(|| {
println!("hello from the second scoped thread");
// 没有其它线程在使用,所以也可以可变借用 `x`
x += a[0] + a[2];
});
println!("hello from the main thread");
});
// Scope 退出后,可以继续修改、访问变量。
a.push(4);
assert_eq!(x, a.len());
```
## Rust 对原始文件描述符/句柄的所有权
之前 Rust 代码在使用平台相关 API 涉及到文件描述符file descriptor on unix或句柄handles on windows的时候都是直接使用对应的描述符比如`c_int` alias `RawFd`)。因此类型系统无法判断 API 是会获取文件描述符的所有权,还是仅仅借用它。
现在Rust 提供了封装类型诸如 `BorrowedFd``OwnedFd`。这些封装类型都标记为了 `#[repr(transparent)]`,意味着 `extern "C"` 绑定下也可以直接使用这些类型来编码所有权语义。完整的封装类型参见原文下的 [stabilized apis in 1.63](https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html#stabilized-apis)
## `Mutex`, `RwLock`, `Condvar` 作为静态变量
`Condvar::new`, `Mutex::new``RwLock::new` 可以在 `const` 上下文里被调用了,不必再使用 `lazy_static` 库来写全局静态的 `Mutex`, `RwLock`, `Condvar` 了。
## Turbofish 可用于含有 `impl Trait` 的泛型函数上
诸如 `fn foo<T>(value: T, f: impl Copy)` 的函数签名,使用 Turbofish `foo::<u32>(3,3)` 来指定 `T` 的具体类型会出现编译错误:
```shell
error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position
--> src/lib.rs:4:11
|
4 | foo::<u32>(3, 3);
| ^^^ explicit generic argument not allowed
|
= note: see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information
```
1.63里这个限制被放松了,显式泛型类型可以用 Turbofish 来指定了。不过 `impl Trait` 参数,尽管已经脱糖(desugare)成了泛型,因为还是不透明的所以无法通过 Turbofish 指定。
## 完成了 Non-lexical-lifetime 的生命周期检查器的迁移
1.63 的rustc完全删除了之前的词法借用检查完全启用了新的 NLL 借用检查器。这不会对编译结果有任何变化,但对编译器的借用错误检查有优化效果。
如果对NLL不了解在本书 [引用与借用](https://course.rs/basic/ownership/borrowing.html#NLL) 一章里有介绍。
或者看官方博客的介绍 [NLL](https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html#non-lexical-lifetimes)
更详细内容可以看原博客 [blog](https://blog.rust-lang.org/2022/08/05/nll-by-default.html)

@ -0,0 +1,125 @@
# Rust 新版解读 | 1.64 | 重点: `IntoFuture` , Cargo 优化
> Rust 1.64 官方 release doc: [Announcing Rust 1.64.0 | Rust Blog](https://blog.rust-lang.org/2022/09/22/Rust-1.64.0.html)
通过 [rustup](https://www.rust-lang.org/tools/install) 安装的同学可以使用以下命令升级到 1.64 版本:
```shell
$ rustup update stable
```
## 使用 `IntoFuture` 增强 `.await`
1.64 稳定了 `IntoFuture` trait不同于用在 `for ... in ...``IntoIterator` trait`IntoFuture` 增强了 `.awiat` 关键字。现在 `.await` 可以 await 除了 futures 外,还可以 await 任何实现了 `IntoFuture` trait 并经此转换成 `Future` 的对象。这可以让你的 api 对用户更加优化。
举一个用在网络存储供应端的例子:
```rust
pub struct Error { ... }
pub struct StorageResponse { ... }:
pub struct StorageRequest(bool);
impl StorageRequest {
/// 实例化一个 `StorageRequest`
pub fn new() -> Self { ... }
/// 是否开启 debug 模式
pub fn set_debug(self, b: bool) -> Self { ... }
/// 发送请求并接受回复
pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}
```
通常地使用方法可能类似如下代码:
```rust
let response = StorageRequest::new() // 1. 实例化
.set_debug(true) // 2. 设置一些选项
.send() // 3. 构造 future
.await?; // 4. 执行 future ,传递 error
```
这个代码已经不错了,不过 1.64 后可以做的更好。使用 `IntoFuture` ,把第三步的 “构造 future ” 和 第四步的 “执行 future ” 合并到一个步骤里:
``` RUST
let response = StorageRequest::new() // 1. 实例化
.set_debug(true) // 2. 设置一些选项
.await?; // 3. 构造并执行 future ,传递 error
```
想要实现上面的效果,我们需要给 `StorageRequest` 实现 `IntoFuture` trait。`IntoFuture` 需要确定好要返回的 future可以用下面的代码来实现
``` rust
// 首先需要引入一些必须的类型
use std::pin::Pin;
use std::future::{Future, IntoFuture};
pub struct Error { ... }
pub struct StorageResponse { ... }
pub struct StorageRequest(bool);
impl StorageRequest {
/// 实例化一个 `StorageRequest`
pub fn new() -> Self { ... }
/// 是否开启 debug 模式
pub fn set_debug(self, b: bool) -> Self { ... }
/// 发送请求并接受回复
pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}
// 新的实现内容
// 1. 定义好返回的 future 类型
pub type StorageRequestFuture = Pin<Box<dyn Future<Output = Result<StorageResponse, Error>> + Send + 'static>>
// 2. 给 `StorageRequest` 实现 `IntoFuture`
impl IntoFuture for StorageRequest {
type IntoFuture = StorageRequestFuture;
type Output = <StorageRequestFuture as Future>::Output;
fn into_future(self) -> Self::IntoFuture {
Box::pin(self.send())
}
}
```
这确实需要多写一点实现代码,不过可以给用户提供一个更简单的 api 。
未来Rust 异步团队 希望能够通过给类型别名提供 `impl Trait` [Type Alias Impl Trait](https://rust-lang.github.io/impl-trait-initiative/explainer/tait.html),来简化定义 futures 实现 `IntoFuture` 的代码;再想办法移除 `Box` 来提升性能。
## `core``alloc` 中和 C 语言兼容的 FFI 类型
当调用 C-ABI 或者调用 C-ABI 的时候Rust 代码通常会使用诸如 `c_uint` 或者 `c_ulong` 的类型别名来匹配目标语言里的对应类型。
在次之前,这些类型别名仅在 `std` 里可用,而在嵌入式或者其它仅能使用 `core` 或者 `alloc` 的场景下无法使用。
1.64 里在 `core::ffi` 里提供了所有 `c_*` 的类型别名,还有 `core::ffi::CStr` 对应 C 的字符串,还有仅用 `alloc` 库情况下可以用 `alloc::ffi::CString` 来对应 C 的字符串。
## 可以通过 rustup 来使用 rust-analyzer
rust-analyzer 现在被加进 Rust 工具集里了。这让在各平台上下载使用 rust-analyzer 更加方便。通过 [rustup component](https://rust-lang.github.io/rustup/concepts/components.html) 来安装:
```shell
rustup component add rust-analyzer
```
目前,使用 rustup 安装的版本,需要这样启用:
```shell
rustup run stable rust-analyzer
```
下一次 rustup 的发布本把会提供一个内置的代理,来运行对应版本的 rust-analyzer 。
## Cargo 优化workspace 继承和多目标构建
当在一个 Cargo workspace 里管理多个相关的库/产品时,现在可以避免在多个库里使用相同的字段值了,比如相同的版本号,仓库链接,`rust-version`。在更新的时候也可以更容易地保持这些信息地一致性。更多细节可以参考:
* [workspace.package](https://doc.rust-lang.org/cargo/reference/workspaces.html#the-package-table)
* [workspace.dependencies](https://doc.rust-lang.org/cargo/reference/workspaces.html#the-dependencies-table)
* ["inheriting a dependency from a workspace"](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#inheriting-a-dependency-from-a-workspace)
另外在构建多个目标地时候,现在可以直接传递多个 `--target` 选项给 `cargo build` 来一次性编译所有目标。也可以在 `.cargo/config.toml` 里设置一个 `build.target` 的 array 来改变默认构建时的对象。
## 稳定API && Others
更多稳定API列表和其它更新内容请参考原文最后 [stabilized-apis](https://blog.rust-lang.org/2022/09/22/Rust-1.64.0.html#stabilized-apis)

@ -0,0 +1,119 @@
# Rust 新版解读 | 1.65 | 重点: 泛型关联类型,新绑定语法!
> Rust 1.65 官方 release doc: [Announcing Rust 1.65.0 | Rust Blog](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html)
通过 [rustup](https://www.rust-lang.org/tools/install) 安装的同学可以使用以下命令升级到 1.65 版本:
```shell
$ rustup update stable
```
## 泛型关联类型 Generic associated types (GATs)
关联类型associated types里现在可以加上生命周期、类型、const泛型了类似于
```rust
trait Foo {
type Bar<'x>;
}
```
三言两语说不清这个变化的好处,看几个例子来感受一下:
```rust
/// 一个类似于 `Iterator` 的 trait ,可以借用 `Self`
trait LendingIterator {
type Item<'a> where Self: 'a;
fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}
/// 可以给智能指针类型,比如 `Rc``Arc` 实现的 trait来实现指针类型的泛用性
trait PointerFamily {
type Pointer<T>: Deref<Target = T>;
fn new<T>(value: T) -> Self::Pointer<T>;
}
/// 允许借用数组对象,对不需要连续存储数据的固定长度数组类型很有用
trait BorrowArray<T> {
type Array<'x, const N: usize> where Self: 'x;
fn borrow_array<'a, const N: usize>(&'a self) -> Self::Array<'a, N>;
}
```
泛型关联类型十分通用,能够写出许多之前无法实现的模式。更多的信息可以参考下面的链接:
* [2021/08/03/GAT稳定版本推进](https://blog.rust-lang.org/2021/08/03/GATs-stabilization-push.html)
* [2022/10/28/GAT稳定版本发布公告](https://blog.rust-lang.org/2022/10/28/gats-stabilization.html)
第一个对上面的例子进行了更深入的讨论,第二个讨论了一些已知的局限性。
更深入的阅读可以在关联类型的 [nightly reference](https://doc.rust-lang.org/nightly/reference/items/associated-items.html#associated-types) 和 [原始 RFC](https://rust-lang.github.io/rfcs/1598-generic_associated_types.html)已经过去6.5年了!) 里找到。
## `let` - `else` 语法
新的 `let` 语法,尝试模式匹配,找不到匹配的情况下执行发散的 `else` 块。
```rust
let PATTERN: TYPE = EXPRESSION else {
DIVERGING_CODE;
};
```
常规的 `let` 语法仅能使用 `irrefutable patterns`,直译为不可反驳的模式,也就是一定要匹配上。一般情况下都是单个变量绑定,也用在解开结构体,元组,数组等复合类型上。原先并不适用条件匹配,比如从枚举里确定枚举值。直到现在我们有了 `let` - `else`。这是 `refutable pattern`,直译为可反驳的模式,能够像常规 `let` 一样匹配并绑定变量到周围范围内,在模式不匹配的时候执行发送的 `else` (可以是 `break`, `return`, `panic!`)。
```rust
fn get_count_item(s: &str) -> (u64, &str) {
let mut it = s.split(' ');
let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
panic!("Can't segment count item pair: '{s}'");
};
let Ok(count) = u64::from_str(count_str) else {
panic!("Can't parse integer: '{count_str}'");
};
(count, item)
}
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));
```
`if` - `else``match` 或者 `if let` 最大不一样的地方是变量绑定的范围,在此之前你需要多写一点重复的代码和一次外层的 `let` 绑定来完成:
```rust
let (count_str, item) = match (it.next(), it.next()) {
(Some(count_str), Some(item)) => (count_str, item),
_ => panic!("Can't segment count item pair: '{s}'"),
};
let count = if let Ok(count) = u64::from_str(count_str) {
count
} else {
panic!("Can't parse integer: '{count_str}'");
};
```
## `break` 跳出标记过的代码块
块表达式现在可以标记为 `break` 的目标,来达到提前终止块的目的。这听起来有点像 `goto` 语法,不过这并不是随意的跳转,只能从块里跳转到块末尾。这在之前已经可以用 `loop` 块来实现了,你可能大概率见过这种总是只执行一次的 `loop`
1.65 可以直接给块语句添加标记来提前退出了,还可以携带返回值:
```rust
let result = 'block: {
do_thing();
if condition_not_met() {
break 'block 1;
}
do_next_thing();
if condition_not_met() {
break 'block 2;
}
do_last_thing();
3
};
```
## Others
其它更新细节和稳定的API列表参考[原Blog](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html)

@ -0,0 +1,112 @@
# Rust 新版解读 | 1.66 | 重点: 有字段枚举的显示判别
> Rust 1.66 官方 release doc: [Announcing Rust 1.66.0 | Rust Blog](https://blog.rust-lang.org/2022/12/15/Rust-1.66.0.html)
通过 [rustup](https://www.rust-lang.org/tools/install) 安装的同学可以使用以下命令升级到 1.66 版本:
```shell
$ rustup update stable
```
## 对有字段枚举的显示判别
枚举的显示判别在跨语言传递值时很关键,需要两个语言里每个枚举值的判别是一致的,比如:
```rust
#[repr(u8)]
enum Bar {
A,
B,
C = 42,
D,
}
```
这个例子里,枚举 `Bar` 使用了 `u8` 作为原语表形(representation),并且 `Bar::C` 使用 42 来判别,其它没有显示判别的枚举值会按照源码里地顺序自动地递增赋值,这里的 `Bar::A` 是0`Bar::B` 是1`Bar::D` 是43。如果没有显示判别那就只能在 `Bar::B``Bar::C` 之间加上 40 个无意义的枚举值了。
在1.66之前,枚举的显示判别只能用在无字段枚举上。现在对有字段枚举的显示判别也稳定了:
```rust
#[repr(u8)]
enum Foo {
A(u8),
B(i8),
C(bool) = 42,
}
```
注意:可以通过 `as` 转换(比如 `Bar::C as u8` )来判断一个无字段枚举的判别值,但是 Rust 还没有给有字段枚举提供语言层面上的获取原始判别值的方法,只能通过 unsafe 的代码来检查有字段枚举的判别值。考虑到这个使用场景往往出现在必须使用 unsafe 代码的跨语言的 FFI 里,希望这没有造成太大的负担。如果你的确需要的话,参考 `std::mem::discriminant`
## 黑盒方法 `core::hint::black_box`
当对编译器产生的代码做基准测试时,常常需要阻止一些优化,比如下面的代码里, `push_cap` 在一个循环里执行了4次 `Vec::push`
```rust
fn push_cap(v: &mut Vec<i32>) {
for i in 0..4 {
v.push(i);
}
}
pub fn bench_push() -> Duration {
let mut v = Vec::with_capacity(4);
let now = Instant::now();
push_cap(&mut v);
now.elapsed()
}
```
如果你检查一下在 x86_64 机器上编译的优化输出结果,你会注意到整个 `push_cap` 方法都被优化掉了...
```text
example::bench_push:
sub rsp, 24
call qword ptr [rip + std::time::Instant::now@GOTPCREL]
lea rdi, [rsp + 8]
mov qword ptr [rsp + 8], rax
mov dword ptr [rsp + 16], edx
call qword ptr [rip + std::time::Instant::elapsed@GOTPCREL]
add rsp, 24
ret
```
现在可以通过调用 `black_box` 来避免类似情况的发送。 虽然实际上 `black_box` 内部只会取走值并直接返回,但是编译器会认为这个方法可能做任何事情。
``` rust
use std::hint::black_box;
fn push_cap(v: &mut Vec<i32>) {
for i in 0..4 {
v.push(i);
black_box(v.as_ptr());
}
}
```
这样就可以得到展开循环的[结果](https://rust.godbolt.org/z/Ws1GGbY6Y)
```text
mov dword ptr [rbx], 0
mov qword ptr [rsp + 8], rbx
mov dword ptr [rbx + 4], 1
mov qword ptr [rsp + 8], rbx
mov dword ptr [rbx + 8], 2
mov qword ptr [rsp + 8], rbx
mov dword ptr [rbx + 12], 3
mov qword ptr [rsp + 8], rbx
```
你还能发现结果里有 `black_box` 带来的副作用,无意义的 `mov qword ptr [rsp + 8], rbx` 指令在每一次循环后出现,用来获取 `v.as_ptr()` 作为参数传递给并未真正使用的方法。
注意到上面的例子里,`push` 指令都不用考虑内存分配的问题,这是因为编译器运行在 `Vec::with_capacity(4)` 的条件下。你可以尝试改动一下 `black_box` 的位置或者在多处使用,来看看其对编译的优化输出的影响。
## `cargo remove`
1.62里我们引入了 `cargo add` 来通过命令行给你的项目增加依赖项。现在可以使用 `cargo remove` 来移除依赖了。
## Others
其它更新细节和稳定的API列表参考[原Blog](https://blog.rust-lang.org/2022/12/15/Rust-1.66.0.html#stabilized-apis)

@ -0,0 +1,46 @@
# Rust 新版解读 | 1.67 | `#[must_use]` in `async fn`
> Rust 1.67 官方 release doc: [Announcing Rust 1.67.0 | Rust Blog](https://blog.rust-lang.org/2023/01/26/Rust-1.67.0.html)
通过 [rustup](https://www.rust-lang.org/tools/install) 安装的同学可以使用以下命令升级到 1.67 版本:
```shell
$ rustup update stable
```
2023新年好大年初五更新的新版本来看看有什么新变化~
## `#[must_use]` 作用于 `async fn`
注明了 `#[must_use]``async` 函数会把该属性应用在返回的 `impl Future` 结果上。`Future` trait 已经注明了 `#[must_use]` ,所以所有实现了 `Future` 的类型都会自动加上 `#[must_use]`
所以在 1.67 版本,编译器会警告返回值没有被使用:
```rust
#[must_use]
async fn bar() -> u32 { 0 }
async fn caller() {
bar().await;
}
```
```text
warning: unused output of future returned by `bar` that must be used
--> src/lib.rs:5:5
|
5 | bar().await;
| ^^^^^^^^^^^
|
= note: `#[warn(unused_must_use)]` on by default
```
## `std::sync::mpsc` 实现更新
标准库里的 mpsc多生产者单消费者 通道自从 1.0 版本就有了,这次版本更新将其实现修改成了基于 [`crossbeam-channel`](https://crates.io/crates/crossbeam-channel)。不涉及到API的变更但是修改了一些已有的bug提升了性能和代码可维护性。用户应该不太会感知到明显的变化。
## Others
其它更新细节和稳定的API列表参考[原Blog](https://blog.rust-lang.org/2023/01/26/Rust-1.67.0.html#stabilized-apis)
Loading…
Cancel
Save