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日

#844Rust 模式

Download history 662/week @ 2024-03-24 296/week @ 2024-03-31 264/week @ 2024-04-07 206/week @ 2024-04-14 258/week @ 2024-04-21 61/week @ 2024-04-28 65/week @ 2024-05-05 142/week @ 2024-05-12 97/week @ 2024-05-19 213/week @ 2024-05-26 33/week @ 2024-06-02 63/week @ 2024-06-09 35/week @ 2024-06-16 7/week @ 2024-06-23 133/week @ 2024-06-30 392/week @ 2024-07-07

每月下载量 567

Apache-2.0

22KB
382

early_returns

简化 Rust 中早期返回和循环中断/继续的宏

动机

当与 Option 或 Result 值一起工作时,如果选项未激活(例如,如果它是 None 或错误),则使用早期返回“退出”通常可以提高代码可读性。但是,如果函数的返回类型是单元类型,则不能使用 ? 操作符,这可能导致过度冗长的结构,如嵌套 if-let 块。

此外,? 操作符不能用于循环,如果选项是 None 或结果为 Err,则可能很有用。

早期返回真的好吗?

在我看来,是的,但似乎确实有很多争议!

简要地说,我喜欢早期返回,因为

  1. 它使读者一开始就能明显看到需求/先决条件。
    1. 这似乎应该减少认知负荷,并使读者更容易跟随核心逻辑。
    2. 理想情况下,这有助于“分块”,因此读者可以离散地查看流程。
  2. 它减少了过度的缩进。
    1. 这可能是纯粹的个人偏好,但随着代码缩进的增长,我发现它更难跟随。

你是否真的应该使用它?

可能这个不再很有用——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);
  }
}

无运行时依赖项