别只会 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 的几种方法,以及它们各自的作用。

相关推荐
怪我冷i10 小时前
Rust 的impl语法详解
开发语言·后端·rust
geovindu14 小时前
rust: Semaphore Pattern
开发语言·后端·设计模式·rust·代理模式·企业级信号量模式·同步型模式
Thneonl16 小时前
Claude Code 频繁 Compacting?发布次日我根治了 2 个痛点
后端·rust
怪我冷i1 天前
Rust 智能指针 RefCell<T> 与内部可变性模式
rust
YOLOW922 天前
两周五个版本:我用 Rust 写的 AI 数据引擎 PageSift,0.81 系列做了什么
rust
西风测评2 天前
SSH 中文乱码的根源:locale、SendEnv 与 env 请求
rust
unconditional2 天前
我数了下 sdkm 的源码:41 个工具的安装逻辑,四分之三是同一行
rust·开源
niyongsheng2 天前
3D 装箱算法工程化:数据结构、放置启发式与支撑规则
数据结构·算法·rust
Hello.Reader2 天前
tsrs 技术解析用 Rust 移植 TypeScript 7,类型检查还能怎样提速?
开发语言·rust·typescript
王仔小奶2 天前
Rust 正在接管金融圈?深度拆解一份高薪 FinTech 岗位 JD
开发语言·金融·rust