5 个版本 (3 个重大更改)
0.4.0 | 2022年7月27日 |
---|---|
0.3.1 | 2022年6月17日 |
0.3.0 | 2022年6月17日 |
0.2.1 | 2022年6月9日 |
0.1.0 | 2022年6月8日 |
#844 在 Rust 模式
每月下载量 567
22KB
382 行
early_returns
简化 Rust 中早期返回和循环中断/继续的宏
动机
当与 Option 或 Result 值一起工作时,如果选项未激活(例如,如果它是 None
或错误),则使用早期返回“退出”通常可以提高代码可读性。但是,如果函数的返回类型是单元类型,则不能使用 ?
操作符,这可能导致过度冗长的结构,如嵌套 if-let 块。
此外,?
操作符不能用于循环,如果选项是 None
或结果为 Err
,则可能很有用。
早期返回真的好吗?
在我看来,是的,但似乎确实有很多争议!
简要地说,我喜欢早期返回,因为
- 它使读者一开始就能明显看到需求/先决条件。
- 这似乎应该减少认知负荷,并使读者更容易跟随核心逻辑。
- 理想情况下,这有助于“分块”,因此读者可以离散地查看流程。
- 它减少了过度的缩进。
- 这可能是纯粹的个人偏好,但随着代码缩进的增长,我发现它更难跟随。
你是否真的应该使用它?
可能这个不再很有用——let-else
RFC 看起来将实现一个语言特性来完成这里宏提供的所有功能。在此之前,这可能对你有用!
我个人希望 ?
操作符可以对单元类型起作用。
为什么你不应该使用它?
第一次看到这样的宏时可能会让人感到非常困惑——你需要信任宏的名称,或者查看实际的宏来确认返回、中断或继续的条件。当你将它们引入现有的代码库中时,有可能会让其他人感到困惑,所以如果你在考虑添加这些宏,请务必首先与你的团队进行沟通。
本crate提供的内容
本crate希望通过提供能够获取底层类型、从函数返回或立即中断/继续循环的宏来简化此类类型的操作。
Option的宏
some_or_return
- 如果存在,将“提取”一个
Some
值,或者从当前函数返回。(也可以返回一个默认值。)
- 如果存在,将“提取”一个
some_or_break
- 如果存在,将“提取”一个
Some
值,或者中断当前循环(如果没有指定循环的生命周期)或指定的循环(如果指定了循环的生命周期)。
- 如果存在,将“提取”一个
some_or_continue
- 如果存在,将“提取”一个
Some
值,或者继续当前循环(如果没有指定循环的生命周期)或指定的循环(如果指定了循环的生命周期)。
- 如果存在,将“提取”一个
Result的宏
ok_or_return
- 如果存在,将“提取”一个
Ok
值,或者从当前函数返回。(也可以返回一个默认值。)
- 如果存在,将“提取”一个
ok_or_break
- 如果存在,将“提取”一个
Ok
值,或者中断当前循环(如果没有指定循环的生命周期)或指定的循环(如果指定了循环的生命周期)。
- 如果存在,将“提取”一个
ok_or_continue
- 如果存在,将“提取”一个
Ok
值,或者继续当前循环(如果没有指定循环的生命周期)或指定的循环(如果指定了循环的生命周期)。
- 如果存在,将“提取”一个
示例
从函数中提前返回
动机示例可能如下所示
fn print_if_all_available_nested(a: Option<i32>, b: Option<i32>, c: Result<i32, ()>) {
if let Some(a) = a {
if let Some(b) = b {
if let Ok(c) = c {
println!("{a} + {b} + {c} = {}", a + b + c);
}
}
}
}
随着嵌套的加深或复杂化,读者可能会难以理解。通过提前返回,读者可以更轻松地理解函数的逻辑,但没有简单的方法来实现这一点。例如,以下代码可行且易于阅读,但包含大量模板代码。
fn print_if_all_available_verbose(a: Option<i32>, b: Option<i32>, c: Result<i32, ()>) {
let a = if let Some(a) = a {
a
} else {
return;
};
let b = if let Some(b) = b {
b
} else {
return;
};
let c = if let Ok(c) = c {
c
} else {
return;
};
println!("{a} + {b} + {c} = {}", a + b + c);
}
此crate提供的宏可以减少这种模板代码,因此上述代码可以被替换为
use early_returns::{some_or_return, ok_or_return};
fn print_if_all_available_macro(a: Option<i32>, b: Option<i32>, c: Result<i32, ()>) {
let a = some_or_return!(a);
let b = some_or_return!(b);
let c = ok_or_return!(c);
println!("{a} + {b} + {c} = {}", a + b + c);
}
在循环中继续
类似地,也有一些宏可以用来在循环中中断或继续。例如,以下代码
fn something(_v: &i32) {}
fn do_something_with_vec_of_optionals(values: &Vec<Option<i32>>) {
for value in values.iter() {
let value = if let Some(value) = value {
value
} else {
continue;
};
something(value);
}
}
可以被简化为
use early_returns::some_or_continue;
fn something(_v: &i32) {}
fn do_something_with_vec_of_optionals(values: &Vec<Option<i32>>) {
for value in values.iter() {
let value = some_or_continue!(value);
something(value);
}
}