Rust 新版解读 | 1.65 | 重点: 泛型关联类型,新绑定语法!

Rust 1.65 官方 release doc: Announcing Rust 1.65.0 | Rust Blog

通过 rustup 安装的同学可以使用以下命令升级到 1.65 版本:

$ rustup update stable

泛型关联类型 Generic associated types (GATs)

关联类型(associated types)里现在可以加上生命周期、类型、const泛型了,类似于:

#![allow(unused)]
fn main() {
trait Foo {
    type Bar<'x>;
}
}

三言两语说不清这个变化的好处,看几个例子来感受一下:

#![allow(unused)]
fn main() {
/// 一个类似于 `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>;
}
}

泛型关联类型十分通用,能够写出许多之前无法实现的模式。更多的信息可以参考下面的链接:

第一个对上面的例子进行了更深入的讨论,第二个讨论了一些已知的局限性。

更深入的阅读可以在关联类型的 nightly reference原始 RFC(已经过去6.5年了!) 里找到。

let - else 语法

新的 let 语法,尝试模式匹配,找不到匹配的情况下执行发散的 else 块。

#![allow(unused)]
fn main() {
let PATTERN: TYPE = EXPRESSION else {
    DIVERGING_CODE;
};
}

常规的 let 语法仅能使用 irrefutable patterns,直译为不可反驳的模式,也就是一定要匹配上。一般情况下都是单个变量绑定,也用在解开结构体,元组,数组等复合类型上。原先并不适用条件匹配,比如从枚举里确定枚举值。直到现在我们有了 let - else。这是 refutable pattern,直译为可反驳的模式,能够像常规 let 一样匹配并绑定变量到周围范围内,在模式不匹配的时候执行发送的 else (可以是 break, return, panic!)。

#![allow(unused)]
fn main() {
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 - elsematch 或者 if let 最大不一样的地方是变量绑定的范围,在此之前你需要多写一点重复的代码和一次外层的 let 绑定来完成:

#![allow(unused)]
fn main() {
    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 可以直接给块语句添加标记来提前退出了,还可以携带返回值:

#![allow(unused)]
fn main() {
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