Rust学习笔记(基于Rustlings)(2):数据结构与集合

第二章:数据结构与集合

本文是个人用于学习Rust时的笔记和记录。基于Rustlings练习。


1. 向量 (Vec) (§8.1)

参考章节:

1.1 向量(Vec)基础

向量是Rust中最常用的集合类型,类似C++的std::vector。存储在堆上,可以动态增长。

rust 复制代码
// 创建向量
let v: Vec<i32> = Vec::new();           // 空向量
let v = vec![1, 2, 3];                  // 使用宏创建并初始化

// 添加元素
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);

// 访问元素
let third = &v[2];                      // 索引访问(可能panic)
let third = v.get(2);                   // 安全访问,返回Option

// 获取长度
println!("Length: {}", v.len());

向量 vs 数组

特性 数组 (Array) 向量 (Vec)
存储位置
大小 固定,编译时确定 动态,可增长
类型 [T; N] Vec<T>
性能 更快 略慢

1.2 迭代器

Rust的迭代器类似C++的STL迭代器,但更安全。

rust 复制代码
// 不可变迭代器
for element in &v {
    println!("{}", element);
}

// 可变迭代器
for element in v.iter_mut() {
    *element *= 2;  // 通过解引用修改元素
}

// 迭代器适配器
let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();

常用迭代器方法

  • iter():不可变迭代器
  • iter_mut():可变迭代器
  • map():转换每个元素
  • filter():过滤元素
  • collect():收集为集合
  • take():取前N个元素

1.3 练习解析

vecs1.rs - 创建向量

rust 复制代码
fn array_and_vec() -> ([i32; 4], Vec<i32>) {
    let a = [10, 20, 30, 40]; // 数组
    let v = vec![10, 20, 30, 40]; // 向量

    (a, v)
}

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

    #[test]
    fn test_array_and_vec_similarity() {
        let (a, v) = array_and_vec();
        assert_eq!(a, v[..]);  // 向量切片与数组比较
    }
}

考点

  • vec! 宏创建向量
  • 向量切片 v[..] 与数组比较

vecs2.rs - 向量迭代与修改

rust 复制代码
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
    for element in v.iter_mut() {
        *element *= 2;  // 通过解引用修改元素
    }
    v
}

fn vec_map(v: &Vec<i32>) -> Vec<i32> {
    v.iter().map(|element| {
        element * 2
    }).collect()  // 收集迭代器结果
}

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

    #[test]
    fn test_vec_loop() {
        let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
        let ans = vec_loop(v.clone());
        assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
    }
}

考点

  • iter_mut():可变迭代器,可以修改元素
  • *element:解引用修改值
  • 迭代器链式调用:filter(), take(), collect()

2. 字符串 (String) (§8.2)

参考章节:

2.1 字符串类型

Rust有两种字符串类型,类似C++的std::stringconst char*

类型 说明 存储位置 所有权
String 可增长字符串 拥有所有权
&str 字符串切片 栈/堆引用 借用

2.2 创建字符串

rust 复制代码
let s = String::new();                  // 空字符串
let s = String::from("hello");          // 从字面量创建
let s = "hello".to_string();            // 转换
let s = "hello".to_owned();             // 转换
let s = format!("Hello, {}!", name);    // 格式化

2.3 字符串操作

rust 复制代码
let mut s = String::from("Hello");
s.push_str(" world");                   // 追加字符串切片
s.push('!');                            // 追加字符
s.replace("Hello", "Hi");               // 替换
s.trim();                               // 去除首尾空白
s.to_uppercase();                       // 转大写
s.to_lowercase();                       // 转小写

2.4 字符串切片

rust 复制代码
let s = String::from("hello world");
let hello = &s[0..5];                   // "hello"
let world = &s[6..11];                  // "world"

2.5 练习解析

strings1.rs - 创建String

rust 复制代码
fn current_favorite_color() -> String {
    String::from("blue")  // 使用String::from创建
}

考点String::from() 从字符串字面量创建String

strings2.rs - String与&str转换

rust 复制代码
fn main() {
    let word = String::from("green");
    if is_a_color_word(&word) {  // 传递String的引用(&str)
        println!("That is a color word I know!");
    }
}

fn is_a_color_word(attempt: &str) -> bool {
    attempt == "green" || attempt == "blue" || attempt == "red"
}

考点

  • &word:将String转换为&str
  • 函数参数使用 &str 可以同时接受String和&str

strings3.rs - 字符串操作

rust 复制代码
fn trim_me(input: &str) -> String {
    let a = input.trim_matches(' ');
    a.to_string()  // &str转换为String
}

fn compose_me(input: &str) -> String {
    let mut s = String::from(input);
    s = s + " world!";  // 字符串拼接
    s
}

fn replace_me(input: &str) -> String {
    let s = String::from(input);
    let s1 = s.replace("cars", "balloons");  // 替换
    s1
}

考点

  • trim_matches():去除指定字符
  • .to_string():&str转String
  • + 运算符:字符串拼接(会转移所有权)
  • .replace():字符串替换

strings4.rs - String与&str辨析

rust 复制代码
fn string_slice(arg: &str) {
    println!("{}", arg);
}
fn string(arg: String) {
    println!("{}", arg);
}

fn main() {
    string_slice("blue");                    // &str
    string("red".to_string());               // String
    string(String::from("hi"));              // String
    string("rust is fun!".to_owned());       // String
    string("nice weather".into());           // String
    string(format!("Interpolation {}", "Station"));  // String
    string_slice(&String::from("abc")[0..1]); // &str切片
    string_slice("  hello there ".trim());   // &str
    string("Happy Monday!".to_string().replace("Mon", "Tues"));  // String
    string("mY sHiFt KeY iS sTiCkY".to_lowercase());  // String
}

考点

  • to_string(), to_owned(), into(), String::from() 都可以创建String
  • format!() 宏创建String
  • 字符串切片 &s[0..1] 是&str
  • .trim() 返回&str
  • .replace(), .to_lowercase() 返回String

3. 哈希映射 (HashMap) (§8.3)

参考章节:

3.1 HashMap基础

HashMap存储键值对,类似C++的std::unordered_map

rust 复制代码
use std::collections::HashMap;

// 创建HashMap
let mut scores: HashMap<String, i32> = HashMap::new();

// 插入键值对
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

// 访问值
let score = scores.get("Blue");  // 返回Option<&i32>

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

// 检查键是否存在
if scores.contains_key("Blue") {
    // ...
}

// 获取长度
println!("Length: {}", scores.len());

// 获取所有值的总和
let total: i32 = scores.values().sum();

3.2 Entry API

Entry API是HashMap的高级用法,类似C++的try_emplace

rust 复制代码
// 如果键不存在,插入默认值
scores.entry(String::from("Blue")).or_insert(10);

// 如果键不存在,插入并返回可变引用
let count = scores.entry(String::from("Blue")).or_insert(0);
*count += 1;

3.3 练习解析

hashmaps1.rs - 创建HashMap

rust 复制代码
use std::collections::HashMap;

fn fruit_basket() -> HashMap<String, u32> {
    let mut basket = HashMap::new();

    basket.insert(String::from("banana"), 2);
    basket.insert(String::from("apple"), 4);
    basket.insert(String::from("mango"), 2);

    basket
}

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

    #[test]
    fn at_least_three_types_of_fruits() {
        let basket = fruit_basket();
        assert!(basket.len() >= 3);
    }

    #[test]
    fn at_least_five_fruits() {
        let basket = fruit_basket();
        assert!(basket.values().sum::<u32>() >= 5);
    }
}

考点

  • HashMap::new() 创建HashMap
  • .insert() 插入键值对
  • .len() 获取长度
  • .values().sum() 获取值的总和

hashmaps2.rs - Entry API

rust 复制代码
use std::collections::HashMap;

#[derive(Hash, PartialEq, Eq)]
enum Fruit {
    Apple,
    Banana,
    Mango,
    Lychee,
    Pineapple,
}

fn fruit_basket(basket: &mut HashMap<Fruit, u32>) {
    let fruit_kinds = vec![
        Fruit::Apple,
        Fruit::Banana,
        Fruit::Mango,
        Fruit::Lychee,
        Fruit::Pineapple,
    ];

    for fruit in fruit_kinds {
        // 如果键不存在,插入默认值10
        basket.entry(fruit).or_insert(10);
    }
}

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

    #[test]
    fn test_given_fruits_are_not_modified() {
        let mut basket = get_fruit_basket();
        fruit_basket(&mut basket);
        assert_eq!(*basket.get(&Fruit::Apple).unwrap(), 4);  // 未修改
    }
}

考点

  • #[derive(Hash, PartialEq, Eq)]:派生宏,使枚举可用作HashMap的键
  • .entry().or_insert():Entry API,键不存在时插入默认值
  • .get() 返回 Option<&V>,需要 .unwrap() 获取值
  • * 解引用:将 &u32 转换为 u32

hashmaps3.rs - 复杂HashMap操作

rust 复制代码
use std::collections::HashMap;

struct Team {
    goals_scored: u8,
    goals_conceded: u8,
}

fn build_scores_table(results: String) -> HashMap<String, Team> {
    let mut scores: HashMap<String, Team> = HashMap::new();

    for r in results.lines() {
        let v: Vec<&str> = r.split(',').collect();
        let team_1_name = v[0].to_string();
        let team_1_score: u8 = v[2].parse().unwrap();
        let team_2_name = v[1].to_string();
        let team_2_score: u8 = v[3].parse().unwrap();

        // Entry API + 可变引用修改值
        let count = scores.entry(team_1_name.clone()).or_insert(Team{goals_scored:0, goals_conceded:0});
        count.goals_scored += team_1_score;
        count.goals_conceded += team_2_score;

        let count = scores.entry(team_2_name.clone()).or_insert(Team{goals_scored:0, goals_conceded:0});
        count.goals_scored += team_2_score;
        count.goals_conceded += team_1_score;
    }
    scores
}

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

    #[test]
    fn build_scores() {
        let scores = build_scores_table(get_results());
        let mut keys: Vec<&String> = scores.keys().collect();
        keys.sort();  // 排序
        assert_eq!(
            keys,
            vec!["England", "France", "Germany", "Italy", "Poland", "Spain"]
        );
    }
}

考点

  • 字符串解析:.split(','), .collect(), .parse().unwrap()
  • .lines():按行分割字符串
  • .keys():获取所有键
  • .sort():排序向量
  • Entry API + 可变引用:修改已存在的值

常见错误与避坑指南

❌ 错误1:字符串切片边界错误

rust 复制代码
let s = String::from("hello");
let slice = &s[0..10];  // ❌ panic! 越界

修复:确保切片范围在字符串长度内

❌ 错误2:HashMap键类型错误

rust 复制代码
let mut map = HashMap::new();
map.insert("key", 1);
map.get("ke");  // ❌ 类型不匹配

修复 :使用 map.get(&"key".to_string()) 或确保类型一致

❌ 错误3:向量索引越界

rust 复制代码
let v = vec![1, 2, 3];
let x = v[5];  // ❌ panic! 越界

修复 :使用 .get() 方法,返回 Option<&T>

❌ 错误4:字符串拼接所有权转移

rust 复制代码
let s1 = String::from("hello");
let s2 = s1 + " world";
println!("{}", s1);  // ❌ 编译错误!s1已被移动

修复 :使用 format!() 宏或先克隆

相关推荐
心中有国也有家1 小时前
AtomGit Flutter 鸿蒙客户端:呼吸球动画
学习·flutter·华为·harmonyos
进阶的猪1 小时前
Linux 内核移植过程学习
linux·学习
A.零点2 小时前
期末复习,408考研数据结构:第一章绪论完整知识梳理与真题深度解读
c语言·数据结构·笔记·考研
SteveSenna2 小时前
aubo学习:设置TCP
学习
茯苓gao2 小时前
嵌入式开发笔记:工业机器人通信协议深度解析——从现场总线到工业以太网
笔记·嵌入式硬件·学习·机器人·信息与通信
取地址符2 小时前
Rust语法学习 (基于Rustlings)(1):基础语法
经验分享·笔记·学习·rust
绝世番茄2 小时前
VisualEffect 视觉特效鸿蒙HarmonyOS ArkTS原生学习
学习·华为·harmonyos·鸿蒙
不会调制解调的猫2 小时前
笔记 | 什么是 rp_filter(反向路径过滤)?
服务器·网络·笔记
星马梦缘3 小时前
ACM CSP竞赛笔记(二十三)——线段树 与 树状数组
笔记·线段树·acm·树状数组·csp