Menu
CoddyTech

再次探讨 Option 枚举

本课属于 Coddy Rust 学习旅程的“面向对象编程”单元。第 17 课,共 61 课。

你已经在 Rust 中接触过 Option<T>:它是用于表示值可能存在也可能不存在的标准枚举。现在,让我们从 OOP 的视角来看看它:Option 不只是一个枚举,它还是一种类型,内置了强大的方法,让你能够安全而优雅地处理可空值。

你可以直接对 Option 值调用方法,而不必每次都进行匹配。unwrap_or 方法会在内部值存在时返回该值,否则返回你提供的默认值:

let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name);  // Alice

let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback);  // Guest

如果内部值存在,map 方法会对其进行转换;如果是 None,则保持不变。这样你就可以链接操作,而不必一直检查是否为 None:

let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled);  // Some(10)

let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing);  // None

这些方法体现了 OOP 原则:Option 类型封装了“可能有一个值”这一概念,并提供了一个简洁的接口来处理它。你可以调用对象上的方法,而不是编写外部逻辑来检查它。

challenge icon

挑战

简单

让我们使用 Option 方法构建一个用户资料系统,优雅地处理可选数据。用户可能设置了昵称,也可能没有。你的系统会优雅地处理这两种情况,无需显式匹配。

你将创建两个文件来组织代码:

  • profile.rs:定义一个包含两个字段的公共 Profile 结构体:username(String)和 nickname(Option<String>)。实现以下内容:
    • 一个 new 构造函数,接收用户名和可选的昵称
    • 一个 display_name 方法,如果昵称存在则返回昵称,否则返回用户名作为备用值:使用 unwrap_or 来处理
    • 一个 formatted_nickname 方法,返回一个 Option<String>:如果昵称存在,就将昵称用方括号括起来(例如 "[CoolNick]");如果没有昵称,则返回 None。使用 map 转换这个值
  • main.rs:引入你的资料模块,并创建两个用户资料:一个有昵称,一个没有。分别对每个资料调用这两个方法,展示 Option 方法如何以不同方式处理存在的值和缺失的值。

对于 formatted_nickname 方法,打印时使用调试格式({:?}),因为它返回一个 Option。

你的输出应遵循以下确切格式,展示两个用户资料:

Display name: {name}
Formatted nickname: {option}
Display name: {name}
Formatted nickname: {option}

例如,一个用户名为 "alice"、昵称为 "Ace" 的用户资料,以及另一个用户名为 "bob"、没有昵称的用户资料,输出将是:

Display name: Ace
Formatted nickname: Some("[Ace]")
Display name: bob
Formatted nickname: None

你将收到三个输入:第一个用户名、第一个用户的昵称,以及第二个用户名(该用户没有昵称)。

亲手试试

mod profile;

use profile::Profile;

fn main() {
    // 读取输入
    let mut input1 = String::new();
    std::io::stdin().read_line(&mut input1).expect("Failed to read line");
    let username1 = input1.trim().to_string();

    let mut input2 = String::new();
    std::io::stdin().read_line(&mut input2).expect("Failed to read line");
    let nickname1 = input2.trim().to_string();

    let mut input3 = String::new();
    std::io::stdin().read_line(&mut input3).expect("Failed to read line");
    let username2 = input3.trim().to_string();

    // TODO: 用 username1 和 nickname1(Some)创建第一个用户资料
    
    // TODO: 用 username2 创建第二个用户资料,不设置昵称(None)
    
    // TODO: 输出第一个用户资料的 display_name 和 formatted_nickname
    // formatted_nickname 返回 Option,因此用 {:?} 输出
    
    // TODO: 输出第二个用户资料的 display_name 和 formatted_nickname
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Rust 在线编译器