常用数据结构
Rust 标准库提供了多种有用的数据结构,称为集合(collections)。与内置的数组和元组不同,集合存储在堆上,大小可以动态变化。
String 与 &str
Section titled “String 与 &str”Rust 有两种主要的字符串类型:
| 类型 | 说明 | 存储位置 |
|---|---|---|
String | 可变、可增长的字符串 | 堆 |
&str | 字符串切片,不可变 | 通常是堆或静态存储区 |
创建 String
Section titled “创建 String”fn main() { // 方法 1:从字符串字面量创建 let s1 = String::from("hello");
// 方法 2:使用 to_string() let s2 = "hello".to_string();
// 方法 3:空字符串 let s3 = String::new();
// 方法 4:使用 into() let s4: String = "hello".into();}String 操作
Section titled “String 操作”fn main() { let mut s = String::from("hello");
// 追加字符串 s.push_str(", world");
// 追加字符 s.push('!');
println!("{}", s); // hello, world!
// 字符串长度(字节数) println!("长度: {}", s.len());
// 是否为空 println!("是否为空: {}", s.is_empty());
// 清空 s.clear();}fn main() { // 使用 + 运算符 let s1 = String::from("Hello, "); let s2 = String::from("world!"); let s3 = s1 + &s2; // 注意:s1 被移动,不能再使用
// 使用 format! 宏(推荐,不会移动所有权) let s1 = String::from("Hello"); let s2 = String::from("world"); let s3 = format!("{}, {}!", s1, s2); // s1 和 s2 仍然可用
println!("{}", s3);}Rust 字符串是 UTF-8 编码的,不能直接索引:
fn main() { let s = String::from("你好世界");
// 错误!不能索引 // let c = s[0];
// 按字符遍历 for c in s.chars() { println!("{}", c); }
// 按字节遍历 for b in s.bytes() { println!("{}", b); }
// 获取子串(按字节索引,必须在字符边界) let hello = &s[0..6]; // "你好"(每个中文字符 3 字节) println!("{}", hello);}String 与 &str 转换
Section titled “String 与 &str 转换”fn main() { // &str -> String let s1: String = "hello".to_string(); let s2: String = String::from("hello");
// String -> &str let s3: &str = &s1; let s4: &str = s1.as_str();
// 函数参数推荐使用 &str fn print_str(s: &str) { println!("{}", s); }
print_str("hello"); // &str print_str(&s1); // String 自动转换为 &str}Vec 动态数组
Section titled “Vec 动态数组”Vec<T> 是可变长度的数组,元素类型相同。
创建 Vec
Section titled “创建 Vec”fn main() { // 方法 1:使用 new let v1: Vec<i32> = Vec::new();
// 方法 2:使用 vec! 宏 let v2 = vec![1, 2, 3];
// 方法 3:指定容量 let v3: Vec<i32> = Vec::with_capacity(10);}Vec 操作
Section titled “Vec 操作”fn main() { let mut v = vec![1, 2, 3];
// 添加元素 v.push(4); v.push(5);
// 移除并返回最后一个元素 let last = v.pop(); // Some(5)
// 获取长度和容量 println!("长度: {}, 容量: {}", v.len(), v.capacity());
// 访问元素 let third = &v[2]; // 直接索引,越界会 panic let third = v.get(2); // 返回 Option,越界返回 None
// 修改元素 v[0] = 10;
// 插入和删除 v.insert(1, 15); // 在索引 1 处插入 v.remove(1); // 删除索引 1 处的元素}遍历 Vec
Section titled “遍历 Vec”fn main() { let v = vec![100, 32, 57];
// 不可变遍历 for i in &v { println!("{}", i); }
// 可变遍历 let mut v = vec![100, 32, 57]; for i in &mut v { *i += 50; }
// 带索引遍历 for (index, value) in v.iter().enumerate() { println!("{}: {}", index, value); }}Vec 与切片
Section titled “Vec 与切片”fn main() { let v = vec![1, 2, 3, 4, 5];
// 获取切片 let slice = &v[1..3]; // [2, 3]
// 函数接收切片更灵活 fn sum(numbers: &[i32]) -> i32 { numbers.iter().sum() }
println!("sum: {}", sum(&v)); println!("sum: {}", sum(&v[1..4]));}HashMap<K, V>
Section titled “HashMap<K, V>”HashMap 存储键值对,通过键快速查找值。
创建 HashMap
Section titled “创建 HashMap”use std::collections::HashMap;
fn main() { // 方法 1:使用 new let mut scores: HashMap<String, i32> = HashMap::new();
// 方法 2:从迭代器创建 let teams = vec![String::from("Blue"), String::from("Yellow")]; let initial_scores = vec![10, 50]; let scores: HashMap<_, _> = teams.into_iter().zip(initial_scores.into_iter()).collect();}HashMap 操作
Section titled “HashMap 操作”use std::collections::HashMap;
fn main() { let mut scores = HashMap::new();
// 插入 scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50);
// 获取 let team_name = String::from("Blue"); let score = scores.get(&team_name); // 返回 Option<&V>
match score { Some(s) => println!("Blue 队得分: {}", s), None => println!("没有找到"), }
// 遍历 for (key, value) in &scores { println!("{}: {}", key, value); }}更新 HashMap
Section titled “更新 HashMap”use std::collections::HashMap;
fn main() { let mut scores = HashMap::new();
// 覆盖旧值 scores.insert(String::from("Blue"), 10); scores.insert(String::from("Blue"), 25); // 覆盖为 25
// 只在键不存在时插入 scores.entry(String::from("Yellow")).or_insert(50); scores.entry(String::from("Yellow")).or_insert(100); // 不会插入
// 基于旧值更新 let text = "hello world wonderful world"; let mut word_count = HashMap::new();
for word in text.split_whitespace() { let count = word_count.entry(word).or_insert(0); *count += 1; }
println!("{:?}", word_count);}Option
Section titled “Option”Option<T> 用于表示可能存在或不存在的值:
enum Option<T> { Some(T), None,}使用 Option
Section titled “使用 Option”fn main() { // 有值 let some_number = Some(5); let some_string = Some("hello");
// 无值(必须指定类型) let absent_number: Option<i32> = None;
// 从 Vec 获取元素 let v = vec![1, 2, 3]; let third = v.get(2); // Some(&3) let tenth = v.get(10); // None}处理 Option
Section titled “处理 Option”fn main() { let x: Option<i32> = Some(5);
// 方法 1:match match x { Some(n) => println!("值是: {}", n), None => println!("没有值"), }
// 方法 2:if let if let Some(n) = x { println!("值是: {}", n); }
// 方法 3:unwrap(有值返回,None 则 panic) let n = x.unwrap();
// 方法 4:unwrap_or(提供默认值) let n = x.unwrap_or(0);
// 方法 5:unwrap_or_else(延迟计算默认值) let n = x.unwrap_or_else(|| { println!("计算默认值"); 0 });
// 方法 6:map(转换 Some 中的值) let doubled = x.map(|n| n * 2); // Some(10)
// 方法 7:and_then(链式调用,类似 flatMap) let result = x.and_then(|n| Some(n * 2));}Result<T, E>
Section titled “Result<T, E>”Result<T, E> 用于表示可能成功或失败的操作:
enum Result<T, E> { Ok(T), Err(E),}使用 Result
Section titled “使用 Result”use std::fs::File;use std::io::Read;
fn main() { // 打开文件可能失败 let file_result = File::open("hello.txt");
let file = match file_result { Ok(f) => f, Err(e) => { println!("打开文件失败: {}", e); return; } };}处理 Result
Section titled “处理 Result”use std::fs::File;
fn main() { // unwrap:成功返回值,失败 panic let f = File::open("hello.txt").unwrap();
// expect:与 unwrap 类似,但可以自定义错误信息 let f = File::open("hello.txt").expect("无法打开文件");
// unwrap_or_else:失败时执行闭包 let f = File::open("hello.txt").unwrap_or_else(|error| { panic!("打开文件时出错: {:?}", error); });
// is_ok / is_err let result = File::open("hello.txt"); if result.is_ok() { println!("文件存在"); }}Option 与 Result 转换
Section titled “Option 与 Result 转换”fn main() { // Option -> Result let x: Option<i32> = Some(5); let result: Result<i32, &str> = x.ok_or("没有值");
// Result -> Option let result: Result<i32, &str> = Ok(5); let option: Option<i32> = result.ok();}练习 1:String 转换
Section titled “练习 1:String 转换”使用三种不同的方法将 &str 转换为 String:
fn main() { let s: &str = "hello";
// 方法 1 // 方法 2 // 方法 3}练习 2:Vec 操作
Section titled “练习 2:Vec 操作”创建一个 Vec,实现以下操作:
- 添加 5 个元素
- 删除最后一个元素
- 在索引 2 处插入一个元素
- 遍历并打印所有元素
fn main() { let mut v: Vec<i32> = Vec::new(); // 你的代码}练习 3:词频统计
Section titled “练习 3:词频统计”使用 HashMap 统计以下文本中每个单词出现的次数:
use std::collections::HashMap;
fn main() { let text = "hello world hello rust world rust rust"; let mut word_count: HashMap<&str, i32> = HashMap::new();
// 你的代码
println!("{:?}", word_count); // 应该输出类似: {"hello": 2, "world": 2, "rust": 3}}练习 4:安全获取元素
Section titled “练习 4:安全获取元素”编写一个函数,从 Vec 中安全获取指定索引的元素:
fn safe_get(v: &Vec<i32>, index: usize) -> Option<i32> { // 你的代码}
fn main() { let v = vec![1, 2, 3]; println!("{:?}", safe_get(&v, 1)); // Some(2) println!("{:?}", safe_get(&v, 10)); // None}练习 5:字符串解析
Section titled “练习 5:字符串解析”编写一个函数,尝试将字符串解析为数字:
fn parse_number(s: &str) -> Result<i32, std::num::ParseIntError> { // 你的代码}
fn main() { println!("{:?}", parse_number("42")); // Ok(42) println!("{:?}", parse_number("hello")); // Err(...)}- The Rust Book - 常用集合
- std::vec::Vec 文档
- std::collections::HashMap 文档
- std::string::String 文档
- std::option::Option 文档
- std::result::Result 文档
了解了常用数据结构后,下一章我们将学习结构体和枚举,它们是自定义类型的基础。