前几天提供了 Rust 版本 1.65.0 Rustのlet-else文気持ち良すぎだろ

其中,我个人最满意的新增功能是let-else这句话!

let-else这句话长什么样

let-else 句子看起来像这样。

let-else 语句
// let 論駁可能パターン = 値 else { never型を返す処理 };
let Ok(val) = reqwest::get(url).await else { return };

这段代码的意思是如果reqwest::get(url).await返回Ok(結果),绑定到val,如果失败则退出函数。

if-let 表达式

在深入了解let-else 语句的细节之前,让我们先讨论一下Rust 的if-let 表达式。

由于 Rust 是一种面向表达式的语言,if 默认也是一个表达式。由于使用了三元运算符,在其他语言中经常会爆发宗教战争,但我一直认为“if”如果有“表达式”,就不会有冲突……(Tooime)。

fn main() {
    let arg = std::env::args().nth(1).unwrap_or("bar".to_string());

    let val = if &arg == "bar" {
        "hoge".to_string()
    } else {
        "fuga".to_string()
    };

    println!("{}", val);
}

计算每个块中最后一个表达式返回的值,绑定到val。像三元运算符一样使用时,true 时返回的类型必须与 false 时返回的类型相匹配。但是,!(从不)类型被排除在外。 (稍后描述)

而 Rust 独特的 if 表达式具有 if-let 表达式。 Rust 的let 具有模式匹配功能,if-let 表达式匹配模式时为真。在下例1中,当parse方法返回的Result类型变量为Ok(i32型)时,解析结果绑定n作为返回值,绑定valreturn 如果不匹配。

示例 1:Result 类型

示例 1
fn main() {
    let arg = std::env::args().nth(1).unwrap_or("NaN".to_string());

    let val = if let Ok(n) = arg.parse::<i32>() {
        n
    } else {
        println!("Caution!: {} is not a number, please pass a number.", arg);
        return;
    };

    println!("{}^2 = {}", val, val * val);
}

为了更准确地解释它,普通的let 语句只接受无可辩驳的模式(总是成功的绑定),而if-let 表达式也是一个允许绑定可反驳模式的函数。简单来说,let本来就有一个扩展绑定结构和元组的功能,所谓解构赋值,对于具有多种可能性的模式,例如枚举(这称为可反驳模式),是否可以使用if-let 公式接收它?而match 表达式是if-let 表达式的增强版本,它在可反驳的模式和分支上执行多个模式匹配。

示例 2:结构的解构赋值

示例 2
#[derive(Clone, Copy)]
struct Point {
    x: u32,
    y: u32,
}

fn main() {
    let point = Point { x: 10, y: 20 };

    // こんな風に分割代入できる!
    let Point { x, y } = point;

    println!("x = {}, y = {}", x, y);

    // let Point { x: 0, y } = point;
    /* refutable pattern in local binding: `Point { x: 1_u32..=u32::MAX, .. }` not covered
       `let` bindings require an "irrefutable pattern", ...
       と怒られる
    */
    // そんな時はif-letやmatchの出番
    if let Point { x: 0, y } = point {
        println!("x = 0, y = {}", y);
    }
}

在上面的例子中,else 子句可以省略,因为没有使用if-let 表达式的返回值。

if-let 经常与ResultOption 类型一起使用,但它也可以与其他可反驳的模式一起使用,例如前面提到的结构。

示例 3:任意枚举

示例 3
enum MyState {
    Samumi,
    Nemumi,
    Other(String),
}

fn main() {
    let my_state = MyState::Other("Hello".to_string());

    let state_inner = if let MyState::Other(state_inner) = my_state {
        state_inner
    } else {
        println!("Your state is Samumi or Nemumi.");
        return;
    };

    println!("Your state is Other({})", state_inner);
}

但是,对于像 MyState 这样的超过 3 个项目的枚举,您应该使用具有更好前景的 match 公式。

示例 3'
let state_inner = match my_state {
    MyState::Other(state_inner) => state_inner,
    MyState::Samumi | MyState::Nemumi => { // `_ => {` でも可
        println!("Your state is Samumi or Nemumi.");
        return;
    }
};

从不输入(关于ifelse 子句的返回值)

我还将在这里解释!(从不)类型。 (因为我用它)

通常情况下,当使用ifif-let作为“表达式”时,真假值的类型必须匹配。

但是,对于其他语言中常见的“if,return,continue,或者throw Error(假设panic!被指出)”的情况我们应该怎么做呢?示例 1 和示例 3 正是这种情况。

实际上,这些处理语法在 Rust 中被视为返回 ! (never) 类型,而 never 类型为类型强制到其他分支类型这是一种特殊的类型

returncontinue 返回 !(从不)类型(假定),因此对其执行类型推断并且不会发生编译错误。霸道感强

那么let-else 声明是什么?

介绍变得很长,但它终于是主菜了。

let-else 语句的定位类似于“let 可反驳模式的语句”!如果模式匹配,则按原样执行解构,如果不匹配,则执行else 子句的内容。

let-else 语句
let 論駁可能パターン =  else { never型を返す処理 };

查看示例 3 中的 if-let 表达式。并计算state_inner 的数量。

let state_inner = if let MyState::Other(state_inner) = my_state {
    state_inner
} else {
    println!("Your state is Samumi or Nemumi.");
    return;
};

3,不是吗?我写了三遍state_inner我只是想提取Other的内容,但是我写了很多次state_inner.很明显,这个if-let 表达式后面是使用state_inner 的主要处理。这一行只是else 子句。

如果你使用let-else这句话,你就完全没有这样的烦恼了!

let MyState::Other(state_inner) = my_state else {
    println!("Your state is Samumi or Nemumi.");
    return;
};

感觉很好!

它在语义上也完全不同并且写得更好。

  • if-let公式:真时间分支和假时间分支平等地位两种情况同样重要
  • let-else语句:真时间分支是正常时间处理,else子句处理异常系统并且通常不会发生

对于需要if-let 的场景,let-else 可能是最佳选择。添加了非常感谢的语法。

注意:else 子句永远不是类型

let-elseelse 部分中,您必须返回前面解释过的 never 类型。换句话说,returncontinuepanic! 宏等必须在末尾用发散表达式关闭。

我认为出现了一个合理的问题,“当您想要返回默认值之类的东西时?”

  • 使用ResultOption 类型:unwrap_or
  • 其他枚举:使用if-letmatch表达式

让我们像往常一样处理它。

let-else 语句用例

实际上,示例 1 使用? 运算符(try! 宏)和anyhow::Result 编写如下。

示例 1'
fn main() -> anyhow::Result<()> {
    let arg = std::env::args().nth(1).unwrap_or("NaN".to_string());
    let val = arg.parse::<i32>()?;

    println!("{}^2 = {}", val, val * val);

    Ok(())
}

对于Result 类型和Option 类型,当?try 宏)可以不使用if-letmatch 来编写时,习惯上使用?

即使发生错误时要插入一些处理,也可以使用map_err等进行如下操作。

示例 1''
let val = arg.parse::<i32>().map_err(|e| {
    println!("エラー時処理");
    e
})?;

鉴于我之前提到的语义,在许多情况下,在处理既不是Result 也不是Option 的枚举时使用match 更有意义。

哦...?let-else 滥用成为一种反模式...?

我想要for continue

在可以返回Result类型和Option类型的场景中,这些类型的??方法太能干了,添加的let-else语句在很多场景中没有位置。

但是for 句子中的continue 是不同的。不支持return ?let-else我给你找到了工作!你做到了!

示例 4
fn main() {
    let maybe_numbers = vec!["0", "1", "2", "fizz", "4", "buzz"];

    for maybe_number in maybe_numbers {
        let Ok(n) = maybe_number.parse::<i32>() else {
            continue;
        };

        println!("{}^2 = {}", n, n * n);
    }
}

...唔?它是什么?你?是的?你在功能语言领域吗?

示例 4'
fn main() {
    let maybe_numbers = vec!["0", "1", "2", "fizz", "4", "buzz"];
    let pow2numbers: Vec<_> = maybe_numbers
        .into_iter()
        .filter_map(|maybe_number| maybe_number.parse::<i32>().ok())
        .map(|n| n * n)
        .collect();

    println!("result: {:?}", pow2numbers);
}

...那? let-else你? !你去哪儿! @987654444 @你! ! ! ! ! ! ! !

. . .
. . .

我闹了一场闹剧,但我避免了for这句话厨房二病Rustaceans 很少使用for 句子。我忘了。

我想要continue in for 异步语句

Rustacean 喜欢函数式伙计们每个人在让他们闭嘴在一个令人信服的过程中对于异步有。 awaitmapfor_each 非常不兼容(至少我想省略本文中的解释)1所以我认为使用for 进行异步更常见。

如果你想在这样的场景中continuelet-else这句话就会显示出它的真实价值。 !

例 5
use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let sites = [
        "https://google.com",
        "http://localhost:8000",
        "https://yahoo.co.jp",
    ];

    for site in sites.iter() {
        let Ok(res) = reqwest::get(*site).await else { continue };
        println!("{}: {}", site, res.status());

        let Ok(body) = res.text().await else { continue };
        println!("body len: {}", body.len());
    }

    Ok(())
}

(我不想写所以就不写了,不过如果有人不用for也能写出来,如果你能告诉我哪个更好,我将不胜感激。)

用例总结

其他我能想到的还有很多,但是暂时我觉得以下两种情况是可以使用let-else的地方。

  • 当你想continuefor 时(特别是异步你需要使用for
  • 想躺的时候(不擅长的反模式)
    • 不要使用map_err等(挤压发生的错误)在else部分使用return Err(...);
      • map_err 已经够挤了,所以我觉得很虔诚。
    • 当您不希望函数的返回类型为Result 并返回默认值而不是错误时
        我认为您应该考虑
      • ...Result 类型。
    • 当您在测试中惊慌失措时想做其他事情时

如果每个想出使用let-else 的人都想收到您的评论,我将不胜感激。

摘要/印象

很久以前,let-else这句话是我最想找的答案,在@类型中使用?不是最好吗?我开始思考。即便如此,在for的句子中,Result之类的continue可能会出现无法使用的情况,所以我写这篇文章是为了在这样的场景中表现出它的真正价值。

上一个用例总结中的let-else这句话不是很好,但是在while-let旁边是我最喜欢的语法。

谢谢你读到这里!

引文/参考

  1. https://qiita.com/legokichi/items/4f2c09330f90626600a6 などを見てほしいです。Futurecollectしすべて完了するのを待つみたいな書き方は可能でしょうが、、、高速化させたい等のシーンでないならば素直にループごとにawaitするように書くほうが"早"そうです。


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308633015.html

分类:

技术点:

Rust

相关文章: