使用 Vec 构建列表

RustBeginner
立即练习

简介

变量用于存储一个值,而许多程序需要长度可以变化的有序列表。Rust 的 Vec<T> 是一种可增长的序列:每个元素的类型都是 T,元素会保持原有顺序,并且向量拥有其中的元素。

你将创建一个咖啡馆订单列表,安全读取其中一个位置,在不消耗列表的情况下访问每个订单,并通过可变引用更新存储的每个字符串。

创建并扩展向量

在本步骤中,你将向一个最初为空的向量中添加三个拥有所有权的字符串。

进入准备好的项目并打开源文件:

cd /home/labex/project/cafe-orders
nano src/main.rs

声明 Vec<String> 表示「元素为拥有所有权的 String 值的向量」。这与之前在 Option<u32> 中看到的容器类型尖括号写法相同:这里指定的元素类型是 String。在后续的泛型实验中,你将学习如何定义这类可复用的类型。Vec::new() 会创建一个空向量。绑定使用 mut,因为添加元素会改变向量。

将步骤 1 的注释替换为以下三个 push 调用:

    orders.push(String::from("Latte"));
    orders.push(String::from("Tea"));
    orders.push(String::from("Cake"));

push 会将一个元素追加到末尾,并保持元素的插入顺序。按 Ctrl+O 保存,按 Enter 确认,然后按 Ctrl+X 退出。检查并运行程序:

cargo check
cargo run --quiet

输出应为:

Order count: 3
Orders: ["Latte", "Tea", "Cake"]

len() 报告向量中有三个元素。:? 占位符使用 Rust 的调试格式显示完整向量,其中字符串两侧会包含引号。

安全读取指定位置

在本步骤中,你将读取向量的第二个位置,同时避免因索引超出范围而发生 panic。

向量的位置从零开始计数,因此索引 1 表示第二个订单。get 方法返回 Option<&String>:当指定位置存在时,Some 包含一个共享引用;当位置不存在时,返回 None。这个引用允许你读取元素,同时向量仍然拥有该元素。

打开源文件:

nano src/main.rs

将步骤 2 的注释替换为:

    match orders.get(1) {
        Some(order) => println!("Second order: {order}"),
        None => println!("Second order: missing"),
    }

这段代码读取元素,但不会将其中的 String 移出向量。保存并退出,然后运行:

cargo run --quiet

输出末尾应新增一行:

Second order: Tea

这行输出证明位置 1 存在,并且元素保持了插入顺序。

使用共享引用进行遍历

在本步骤中,你将访问每个订单,同时确保遍历结束后仍可使用该向量。

写出 &orders 表示借用向量,而不是将向量移动到 for 循环中。因此,循环变量每次都是对一个已存储 String 的共享引用。

打开源文件:

nano src/main.rs

将步骤 3 的注释替换为以下循环和后续检查:

    for order in &orders {
        println!("Queued: {order}");
    }
    println!("Stored after display: {}", orders.len());

保存并退出,然后运行:

cargo run --quiet

新增的几行应为:

Queued: Latte
Queued: Tea
Queued: Cake
Stored after display: 3

循环结束后仍可以获取最终数量,因为循环只是借用了向量,并且在进入下一次迭代前归还了每次共享借用。

通过可变引用更新元素

在本步骤中,你将以可变方式借用向量中的每个元素,并在显示循环运行前直接更新这些元素。

同一时间,一个值只能存在一个可变引用。对 &mut orders 使用 for 循环可以满足这一规则:循环每次提供一个元素的可变引用,在当前迭代中使用它,然后继续处理下一个元素。

打开源文件:

nano src/main.rs

将步骤 4 的注释替换为:

    for order in &mut orders {
        order.push_str(" - ready");
    }

push_str 会将借用的文本追加到每个拥有所有权的 String 中。调用此方法时,Rust 会自动使用每个引用背后的可变 String。

保存并退出,然后检查并运行:

cargo check
cargo run --quiet

完整输出应为:

Order count: 3
Orders: ["Latte", "Tea", "Cake"]
Second order: Tea
Queued: Latte - ready
Queued: Tea - ready
Queued: Cake - ready
Stored after display: 3

更新后的队列行证明所有元素都已直接修改。最后的数量证明向量仍然拥有原来的三个字符串。

总结

你创建并扩展了一个 Vec<String>,使用 get 安全访问了指定位置,在不消耗向量的情况下遍历了它,并通过可变引用更新了每个元素。