前几天提供了 Rust 版本 1.65.0
其中,我个人最满意的新增功能是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
作为返回值,绑定val
。 return
如果不匹配。
示例 1:Result
类型
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:结构的解构赋值
#[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
经常与Result
和Option
类型一起使用,但它也可以与其他可反驳的模式一起使用,例如前面提到的结构。
示例 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
公式。
let state_inner = match my_state {
MyState::Other(state_inner) => state_inner,
MyState::Samumi | MyState::Nemumi => { // `_ => {` でも可
println!("Your state is Samumi or Nemumi.");
return;
}
};
从不输入(关于if
的else
子句的返回值)
我还将在这里解释!
(从不)类型。 (因为我用它)
通常情况下,当使用if
或if-let
作为“表达式”时,真假值的类型必须匹配。
但是,对于其他语言中常见的“if
,return
,continue
,或者throw Error
(假设panic!
被指出)”的情况我们应该怎么做呢?示例 1 和示例 3 正是这种情况。
实际上,这些处理语法在 Rust 中被视为返回 !
(never) 类型,而 never 类型为类型强制到其他分支类型这是一种特殊的类型
return
和 continue
返回 !
(从不)类型(假定),因此对其执行类型推断并且不会发生编译错误。霸道感强
那么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-else
的else
部分中,您必须返回前面解释过的 never 类型。换句话说,return
、continue
、panic!
宏等必须在末尾用发散表达式关闭。
我认为出现了一个合理的问题,“当您想要返回默认值之类的东西时?”
-
使用
Result
或Option
类型:unwrap_or
- 其他枚举:使用
if-let
或match
表达式
让我们像往常一样处理它。
let-else
语句用例
实际上,示例 1 使用?
运算符(try!
宏)和anyhow::Result
编写如下。
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-let
或match
来编写时,习惯上使用?
。
即使发生错误时要插入一些处理,也可以使用map_err
等进行如下操作。
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
我给你找到了工作!你做到了!
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);
}
}
...唔?它是什么?你?是的?你在功能语言领域吗?
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 喜欢函数式伙计们每个人在让他们闭嘴在一个令人信服的过程中对于异步有。 await
与map
和for_each
非常不兼容(至少我想省略本文中的解释)1所以我认为使用for
进行异步更常见。
如果你想在这样的场景中continue
,let-else
这句话就会显示出它的真实价值。 !
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
的地方。
-
当你想
continue
和for
时(特别是异步你需要使用for
) - 想躺的时候(不擅长的反模式)
-
不要使用
map_err
等(挤压发生的错误)在else
部分使用return Err(...);
-
map_err
已经够挤了,所以我觉得很虔诚。
-
- 当您不希望函数的返回类型为
Result
并返回默认值而不是错误时- 我认为您应该考虑
- ...
Result
类型。
- ...
- 当您在测试中惊慌失措时想做其他事情时
-
不要使用
如果每个想出使用let-else
的人都想收到您的评论,我将不胜感激。
摘要/印象
很久以前,let-else
这句话是我最想找的答案,在@类型中使用?
不是最好吗?我开始思考。即便如此,在for
的句子中,Result
之类的continue
可能会出现无法使用的情况,所以我写这篇文章是为了在这样的场景中表现出它的真正价值。
上一个用例总结中的let-else
这句话不是很好,但是在while-let
旁边是我最喜欢的语法。
谢谢你读到这里!
引文/参考
-
https://qiita.com/legokichi/items/4f2c09330f90626600a6 などを見てほしいです。
Future
をcollect
しすべて完了するのを待つみたいな書き方は可能でしょうが、、、高速化させたい等のシーンでないならば素直にループごとにawait
するように書くほうが"早"そうです。 ↩
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308633015.html