别只会 unwrap():Rust Result 的几种实用写法

各位 Rust 爱好者们,大家好。

今天我们看点轻松的内容------理解 Rust 的 Result 类型。

一起学习一下,如何使用 Result 表示计算成功或失败,以及如何用它提供的方法处理和转换其中的值。

定义

Result 是 Rust 标准库提供的泛型枚举,用来表示一次计算的结果:成功时是 Ok 变体,失败时是 Err 变体。

Result 的定义如下:

rust 复制代码
enum Result<T, E> {
    Ok(T),
    Err(E),
}

类型参数 T 和 E 分别表示成功值和错误值的类型。也就是说,Result<T, E> 可以表示这样一次计算:成功时返回 T 类型的值,失败时返回 E 类型的错误。

使用示例

下面用 Result 表示一个可能失败的函数的返回结果:

rust 复制代码
fn parse_int(s: &str) -> Result<i32, std::num::ParseIntError> {
    s.parse::<i32>().map_err(|e| e.into())
}

fn main() {
    let result = parse_int("5");
    match result {
        Ok(n) => println!("Parsed integer: {}", n),
        Err(e) => println!("Error parsing integer: {}", e),
    }
}

这个例子中的 parse_int 函数会尝试把字符串解析为整数。解析成功时,返回包含整数的 Ok 变体;解析失败时,返回包含相应 ParseIntError 的 Err 变体。

处理结果

可以用模式匹配处理 Result。上面的例子通过 match 表达式分别处理 Ok 和 Err 变体。也可以用 unwrap 方法取出 Ok 中的值,但如果 Result 是 Err,就会触发 panic:

rust 复制代码
let result = parse_int("5");
let n = result.unwrap(); // Will panic if result is an Err variant
println!("Parsed integer: {}", n);

如果错误可能正常发生,生产代码中应显式处理或传播错误,避免直接调用 unwrap()。如果能够确定结果一定是 Ok,使用 unwrap() 也可以,但应说明这一判断的依据。

如果需要转换 Result 中的值,可以使用 map 和 map_err。

map 只转换 Ok 中的值,保留 Err 不变;map_err 只转换 Err 中的值,保留 Ok 不变。它们返回的仍然是 Result,不会自动处理或消除错误。

例如,下面的 divide 函数对两个整数做除法,并用 Result 表示除法是否成功:

rust 复制代码
fn divide(x: i32, y: i32) -> Result<i32, DivisionError> {
    if y == 0 {
        return Err(DivisionError::DivideByZero);
    }
    Ok(x / y)
}

注:这里省略了 DivisionError 的定义,后面的代码假定它包含 DivideByZero 和 Negative 两个变体。不过,当前函数只检查除数是否为零,不会因为负数返回 Negative。另外,i32::MIN / -1 会触发溢出 panic,因此这个函数并未把所有失败情况都转换成 Err。

map 和 map_err

可以用 map 把除法成功后得到的值转换为其他类型,比如浮点数。这里转换的是整数除法的结果,不会恢复整数除法舍弃的小数部分:

rust 复制代码
let result = divide(10, 2);
let f: Result<f32, DivisionError> = result.map(|n| n as f32);

也可以用 map_err 把错误值转换为其他类型:

rust 复制代码
let result = divide(10, 0);
let f: Result<i32, &str> = result.map_err(|e| match e {
    DivisionError::DivideByZero => "Divide by zero",
    DivisionError::Negative => "Cannot divide by negative number",
});

更多处理 Result 的方法

除了 map 和 map_err,Result 还提供了其他处理和转换值的方法。来看几个例子:

rust 复制代码
fn add_one(x: i32) -> Result<i32, &'static str> {
    if x > 100 {
        return Err("Number too large");
    }
    Ok(x + 1)
}

fn add_two(x: i32) -> Result<i32, &'static str> {
    if x > 50 {
        return Err("Number too large");
    }
    Ok(x + 2)
}

fn add_three(x: i32) -> Result<i32, &'static str> {
    if x > 30 {
        return Err("Number too large");
    }
    Ok(x + 3)
}

// Using and_then
let result = add_one(5).and_then(|x| add_two(x)).and_then(|x| add_three(x));
assert_eq!(result, Ok(11));

// Using or_else
let result = add_one(105).or_else(|e| add_two(5)).or_else(|e| add_three(5));
assert_eq!(result, Ok(7));

// Using unwrap_or
let result = add_one(105).unwrap_or(100);
assert_eq!(result, 100);

// Using unwrap_or_else
let result = add_one(105).unwrap_or_else(|e| -1);
assert_eq!(result, -1);

这里的三个函数各自执行一次运算,并返回 Result。and_then 可以把这些函数串起来。如果所有运算都成功,就返回最后一次运算的结果;任何一步失败,链条就会短路,返回对应的 Err,后续闭包不会执行。

or_else 与 and_then 类似,不过它处理的是 Err 变体。原来的计算失败时,可以通过它指定一次备用计算;如果已经成功,备用计算的闭包就不会执行。

unwrap_or 会返回 Ok 中的值;如果是 Err,则返回传入的默认值。当你希望出错时使用默认值,而不需要显式处理错误时,这个方法就能派上用场。

unwrap_or_else 与 unwrap_or 类似,不过它接收一个闭包。遇到 Err 时,闭包会接收其中的错误值,并计算出默认值。需要根据具体错误决定默认值时,可以用这个方法。

希望这些例子能帮你理解 Rust 中处理和转换 Result 的几种方法,以及它们各自的作用。

相关推荐
DongQiShanRen13 小时前
玄龙(上):TICK 主循环——意识心跳怎么跳
linux·jvm·数据库·人工智能·数据挖掘·rust
编程序的员13 小时前
使用OpenTelemetry来监控Rust应用
运维·rust·monitoring·opentelemetry
对象存储与RustFS1 天前
JuiceFS + 对象存储:把 S3 变成 POSIX 文件系统实测
后端·rust·开源
遇码1 天前
侧边栏一开,AI 画的东西就被挡住了:给白板画布加「真实可见区」计算和相机补偿
人工智能·ai·rust
geovindu1 天前
rust: Borg Pattern(续)
开发语言·后端·设计模式·rust·博格模式
轻舟技术A2 天前
Pi 的 Rust 实现来了:rpi,不止一个终端 Agent
开发语言·后端·rust
wflynn2 天前
GitHub 今日推荐|wordcraft:用 Rust 重写 Word 内核并开放给 AI 调用
rust·开源·github
I'm Jie2 天前
【开源】7 天,我用 Rust 复刻了 FinalShell!Rhost v1.0.0 发布
开发语言·rust·开源
小灰灰搞电子2 天前
Rust+Slint 实现一个终端软件源码分享(半成品)
开发语言·rust