(done) 速通 rustlings(20) 错误处理1 --- 不涉及Traits

RUST 内置 Result, Err, Ok

如下是示例代码:

rust 复制代码
// This function refuses to generate text to be printed on a nametag if
// you pass it an empty string. Instead of returning `None`, it should give
// an error message explaining what went wrong. The caller can then inspect
// the `Result` to see either the generated text or the explanation.
fn generate_nametag_text(name: String) -> Result<String, String> {
    if name.is_empty() {
        // Empty names aren't allowed
        Err("Empty names aren't allowed".to_string())
    } else {
        Ok(format!("Hi! My name is {name}"))
    }
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn generates_nametag_text_for_a_nonempty_name() {
        assert_eq!(
            generate_nametag_text("Beyoncé".to_string()).as_deref(),
            Ok("Hi! My name is Beyoncé"),
        );
    }

    #[test]
    fn explains_why_generating_nametag_text_fails() {
        assert_eq!(
            generate_nametag_text(String::new())
                .as_ref()
                .map_err(|e| e.as_str()),
            Err("Empty names aren't allowed"),
        );
    }
}

? 操作符

RUST,一个函数返回一个 Result 后,并赋值给一个变量时,

rust 复制代码
 let qty = item_quantity.parse::<i32>()?;

中的 "?" 表示:如果 parse 出现错误,提前 return Err(e) 给 caller。

若 parse 未出现错误,则 qty 获取 parse 后的值。

等效于下面的写法:

rust 复制代码
    // Equivalent to this verbose version:
    let qty = match item_quantity.parse::<i32>() {
        Ok(v) => v,
        Err(e) => return Err(e),
    };

完整代码示例如下:

rust 复制代码
// Say we're writing a game where you can buy items with tokens. All items cost
// 5 tokens, and whenever you purchase items there is a processing fee of 1
// token. A player of the game will type in how many items they want to buy, and
// the `total_cost` function will calculate the total cost of the items. Since
// the player typed in the quantity, we get it as a string. They might have
// typed anything, not just numbers!
//
// Right now, this function isn't handling the error case at all. What we want
// to do is: If we call the `total_cost` function on a string that is not a
// number, that function will return a `ParseIntError`. In that case, we want to
// immediately return that error from our function and not try to multiply and
// add.
//
// There are at least two ways to implement this that are both correct. But one
// is a lot shorter!

use std::num::ParseIntError;

#[allow(unused_variables)]
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
    let processing_fee = 1;
    let cost_per_item = 5;

    // Added `?` to propagate the error.
    let qty = item_quantity.parse::<i32>()?;
    //                                    ^ added

    // Equivalent to this verbose version:
    let qty = match item_quantity.parse::<i32>() {
        Ok(v) => v,
        Err(e) => return Err(e),
    };

    let qty = item_quantity.parse::<i32>();

    Ok(qty * cost_per_item + processing_fee)
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::num::IntErrorKind;

    #[test]
    fn item_quantity_is_a_valid_number() {
        assert_eq!(total_cost("34"), Ok(171));
    }

    #[test]
    fn item_quantity_is_an_invalid_number() {
        assert_eq!(
            total_cost("beep boop").unwrap_err().kind(),
            &IntErrorKind::InvalidDigit,
        );
    }
}

易错点1:caller 没有加返回值

RUST 中,如果你调用了一个含有错误处理的函数,且没有处理错误情况而是选择抛出,那么通常 caller 的函数返回值需要加上 Result<..., ...>,如下:

rust 复制代码
// This is a program that is trying to use a completed version of the
// `total_cost` function from the previous exercise. It's not working though!
// Why not? What should we do to fix it?

use std::num::ParseIntError;

// Don't change this function.
fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
    let processing_fee = 1;
    let cost_per_item = 5;
    let qty = item_quantity.parse::<i32>()?;

    Ok(qty * cost_per_item + processing_fee)
}

fn main() -> Result<(), ParseIntError> {
    //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ added
    let mut tokens = 100;
    let pretend_user_input = "8";

    let cost = total_cost(pretend_user_input)?;

    if cost > tokens {
        println!("You can't afford that many!");
    } else {
        tokens -= cost;
        println!("You now have {tokens} tokens.");
    }

    // Added this line to return the `Ok` variant of the expected `Result`.
    Ok(())
}

相关推荐
土豆125018 小时前
Rust宏编程完全指南:用元编程解锁Rust的终极力量
rust·编程语言
小杍随笔1 天前
【Rust 语言编程知识与应用:基础数据类型详解】
开发语言·后端·rust
小杍随笔1 天前
【Rust 语言编程知识与应用:自定义数据类型详解】
开发语言·后端·rust
咚为1 天前
Rust 跨平台编译实战:从手动配置到 Cross 容器化
开发语言·后端·rust
幸福指北1 天前
我用 Tauri + Vue 3 + Rust 开发了一款跨平台网络连接监控工具Portview,性能炸裂!
前端·网络·vue.js·tcp/ip·rust
咚为1 天前
深入浅出 Rust FFI:从内存安全到二进制兼容
开发语言·安全·rust
a1117761 天前
剪切板助手TieZ(开源项目rust)
rust·开源·剪切板
盒马盒马2 天前
Rust:迭代器
开发语言·后端·rust
Source.Liu2 天前
【Iced】stream.rs文件
rust·iced
Kapaseker3 天前
精通 Rust 宏 — 包装新类型
rust