跳转至

集合与迭代器

集合与迭代器

📚 章节概述

本章将介绍 Rust 标准库中的集合类型和迭代器。集合是存储多个值的数据结构,迭代器提供了一种高效、灵活的处理序列数据的方式。

🎯 学习目标

  • 掌握 Vec、HashMap、HashSet 等集合类型
  • 理解迭代器的概念和使用
  • 学会使用迭代器适配器
  • 掌握迭代器的性能特性
  • 了解不同集合的使用场景

📖 Vector

1.1 创建 Vector

Rust
fn main() {
    // 创建空的 Vec
    let v: Vec<i32> = Vec::new();  // Vec<T> 动态数组,存储同类型元素

    // 使用 vec! 宏创建
    let v = vec![1, 2, 3];

    // 指定类型
    let v: Vec<i32> = Vec::new();
}

1.2 添加元素

Rust
fn main() {
    let mut v = Vec::new();
    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);

    println!("{:?}", v);
}

1.3 读取元素

Rust
fn main() {
    let v = vec![1, 2, 3, 4, 5];

    // 使用索引
    let third: &i32 = &v[2];
    println!("The third element is {}", third);

    // 使用 get 方法
    let third: Option<&i32> = v.get(2);
    match third {
        Some(third) => println!("The third element is {}", third),
        None => println!("There is no third element."),
    }
}

1.4 遍历 Vector

Rust
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;
    }
    println!("{:?}", v);
}

1.5 Vector 的所有权

Rust
fn main() {
    let mut v = vec![1, 2, 3, 4, 5];

    // 获取引用
    let first = &v[0];

    // 修改 Vector
    v.push(6); // 编译错误!不能在存在不可变引用时修改

    println!("The first element is: {}", first);
}

1.6 存储不同类型

Rust
fn main() {
    // 使用枚举存储不同类型
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }

    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];
}

📚 字符串

2.1 String 类型

Rust
fn main() {
    // 创建空 String
    let mut s = String::new();

    // 从字面量创建
    let s = String::from("hello");

    // 使用 to_string
    let s = "hello".to_string();

    // 使用字符串字面量
    let s = String::from("你好");
}

2.2 更新字符串

Rust
fn main() {
    let mut s = String::from("foo");
    s.push_str("bar");
    println!("{}", s); // foobar

    let mut s = String::from("lo");
    s.push('l');
    println!("{}", s); // lol

    let s1 = String::from("Hello, ");
    let s2 = String::from("world!");
    let s3 = s1 + &s2; // 注意 s1 被移动了
    println!("{}", s3);
}

2.3 格式化字符串

Rust
fn main() {
    let s1 = String::from("tic");
    let s2 = String::from("tac");
    let s3 = String::from("toe");

    let s = format!("{}-{}-{}", s1, s2, s3);
    println!("{}", s);
}

2.4 字符串切片

Rust
fn main() {
    let hello = "Здравствуйте";

    let s = &hello[0..4]; // 注意:不是字符数,是字节数
    println!("{}", s);
}

2.5 遍历字符串

Rust
fn main() {
    for c in "नमस्ते".chars() {
        println!("{}", c);
    }

    for b in "नमस्ते".bytes() {
        println!("{}", b);
    }
}

🗺️ HashMap

3.1 创建 HashMap

Rust
use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();  // HashMap 键值对集合,O(1)查找

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);

    println!("{:?}", scores);
}

3.2 访问值

Rust
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);

    match score {
        Some(s) => println!("Score: {}", s),
        None => println!("Team not found"),
    }
}

3.3 遍历 HashMap

Rust
use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);

    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }
}

3.4 更新 HashMap

Rust
use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);

    // 覆盖旧值
    scores.insert(String::from("Blue"), 25);

    // 只在键不存在时插入
    scores.entry(String::from("Yellow")).or_insert(50);
    scores.entry(String::from("Blue")).or_insert(50);

    println!("{:?}", scores);
}

3.5 根据旧值更新

Rust
use std::collections::HashMap;

fn main() {
    let text = "hello world wonderful world";

    let mut map = HashMap::new();

    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0); // 返回&mut V(值的可变引用),键不存在则插入0
        *count += 1; // 解引用可变引用后修改HashMap中的值
    }

    println!("{:?}", map);
}

🔄 迭代器

4.1 创建迭代器

Rust
fn main() {
    let mut v = vec![1, 2, 3];

    // 不可变引用迭代器
    for val in v.iter() {
        println!("{}", val);
    }

    // 可变引用迭代器(需要 mut)
    for val in v.iter_mut() {
        *val += 1;
    }

    // 获取所有权(之后 v 不再可用)
    let into_iter = v.into_iter();
    for val in into_iter {
        println!("{}", val);
    }
}

4.2 使用迭代器

Rust
fn main() {
    let v = vec![1, 2, 3];

    // 使用 for 循环
    for val in v.iter() {
        println!("{}", val);
    }

    // 使用 next 方法
    let mut iter = v.iter();
    let val = iter.next();
    println!("{:?}", val); // Some(&1)
}

4.3 迭代器适配器

Rust
fn main() {
    let v1 = vec![1, 2, 3];
    let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();  // |x| 闭包语法,捕获环境变量
    println!("{:?}", v2); // [2, 3, 4]
}

4.4 常用迭代器方法

Rust
fn main() {
    let v = vec![1, 2, 3, 4, 5];

    // map
    let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
    println!("{:?}", doubled);

    // filter
    let evens: Vec<&i32> = v.iter().filter(|&x| x % 2 == 0).collect();
    println!("{:?}", evens);

    // fold
    let sum: i32 = v.iter().fold(0, |acc, x| acc + x);
    println!("Sum: {}", sum);

    // any/all
    let has_even = v.iter().any(|&x| x % 2 == 0);
    let all_positive = v.iter().all(|&x| x > 0);
    println!("Has even: {}, All positive: {}", has_even, all_positive);
}

4.5 消费适配器

Rust
fn main() {
    let v = vec![1, 2, 3, 4, 5];

    // sum
    let sum: i32 = v.iter().sum();
    println!("Sum: {}", sum);

    // collect
    let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
    println!("{:?}", doubled);

    // find
    let found = v.iter().find(|&&x| x > 3);
    println!("Found: {:?}", found);

    // count
    let count = v.iter().count();
    println!("Count: {}", count);
}

📝 练习题

练习 1:Vector 操作

Rust
// TODO: 实现以下功能
// 1. 创建一个包含 1-10 的 Vector
// 2. 过滤出偶数
// 3. 将每个偶数乘以 2
// 4. 计算最终的和

fn main() {
    // TODO: 实现
}

练习 2:字符串处理

Rust
// TODO: 实现一个函数,统计字符串中每个字符的出现次数
fn char_count(s: &str) -> HashMap<char, usize> {
    // TODO: 实现
    todo!()
}

fn main() {
    let s = "hello world";
    println!("{:?}", char_count(s));
}

练习 3:HashMap 操作

Rust
use std::collections::HashMap;

// TODO: 实现一个函数,合并两个 HashMap
// 如果键冲突,保留较大的值
fn merge_maps(map1: HashMap<&str, i32>, map2: HashMap<&str, i32>) -> HashMap<&str, i32> {
    // TODO: 实现
    todo!()
}

fn main() {
    let mut map1 = HashMap::new();
    map1.insert("a", 1);
    map1.insert("b", 2);

    let mut map2 = HashMap::new();
    map2.insert("b", 3);
    map2.insert("c", 4);

    println!("{:?}", merge_maps(map1, map2));
}

练习 4:迭代器链

Rust
// TODO: 使用迭代器实现以下功能
// 1. 生成 1-100 的数字
// 2. 过滤出能被 3 或 5 整除的数
// 3. 将这些数平方
// 4. 取前 10 个
// 5. 计算它们的和

fn main() {
    // TODO: 实现
}

💡 最佳实践

1. 选择合适的集合类型

Rust
// Vec: 需要动态大小、按索引访问
let v = vec![1, 2, 3];

// HashMap: 需要键值对查找
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("key", "value");

// HashSet: 需要唯一值、快速查找
use std::collections::HashSet;
let set: HashSet<i32> = vec![1, 2, 3, 2, 1].into_iter().collect();

2. 使用迭代器而不是循环

Rust
// 好
let sum: i32 = v.iter().sum();
let evens: Vec<i32> = v.iter().filter(|x| x % 2 == 0).cloned().collect();

// 避免
let mut sum = 0;
for x in &v {
    sum += x;
}

3. 函数参数优先使用 &str

Rust
// 好:不需要所有权时接受 &str
fn process(s: &str) {
    println!("{}", s);
}

// 避免:不必要地要求 String 所有权
fn process_owned(s: String) {
    // 强制调用者放弃所有权或克隆
    println!("{}", s);
}

4. 使用 entry API 更新 HashMap

Rust
// 好
scores.entry(team_name).or_insert(0);

// 避免
if !scores.contains_key(&team_name) {
    scores.insert(team_name.clone(), 0);
}

⚠️ 常见错误

1. Vector 索引越界

Rust
// 错误(运行时 panic)
let v = vec![1, 2, 3];
let x = v[10];

// 正确
let v = vec![1, 2, 3];
let x = v.get(10).unwrap_or(&0);

2. 字符串切片错误

Rust
// 错误(可能在字符中间切割)
let s = "你好";
let sub = &s[0..1]; // 可能 panic

// 正确
let s = "你好";
let sub = s.chars().next().unwrap();

3. HashMap 键的类型

Rust
// 可以编译(通过 Deref 强制转换),但有多余的 String 分配
let mut map = HashMap::new();
map.insert("key", "value");
let value = map.get(&String::from("key")); // 可行,但不必要

// 更好:直接使用 &str
let mut map = HashMap::new();
map.insert("key", "value");
let value = map.get("key");

📚 扩展阅读

🎯 本章小结

本章介绍了 Rust 的集合和迭代器:

  • ✅ 掌握 Vec、HashMap、HashSet 等集合类型
  • ✅ 理解迭代器的概念和使用
  • ✅ 学会使用迭代器适配器
  • ✅ 掌握迭代器的性能特性
  • ✅ 了解不同集合的使用场景

下一章: 我们将学习 Rust 的模块系统和包管理,包括模块、use 关键字和 Cargo 依赖管理。