讲解

Vec 是动态数组:堆上存储、长度可变、元素同类型,是 Rust 里使用频率最高的集合。创建用 Vec::new() 或 vec![1, 2, 3] 宏;尾部追加 push(摊还 O(1)),弹出 pop 返回 Option;读取分两种风格:v[i] 下标直取(越界 panic,适合你确信在界内)和 v.get(i)(越界得 None,安全首选)。遍历 for x in &v 拿引用、for x in &mut v 改元素、for x in v 消耗 Vec 拿所有权。

String 本质上是 Vec 包了一层「保证是合法 UTF-8」的壳。创建用 String::new()、String::from("...")、"...".to_string();追加用 push_str(&str) 和 push(char);拼接推荐 format!("{a}{b}") 宏——注意 + 运算符会拿走左操作数的所有权(let s3 = s1 + &s2 之后 s1 失效),链式拼接时 format! 更省心。

UTF-8 带来一个反直觉点:Rust 不允许 s[0] 这样按下标取字符。因为一个「字符」可能占 1 到 4 字节,下标的 O(1) 承诺无法兑现,语言干脆禁止,逼你显式选择:.bytes() 按字节、.chars() 按字符、.len() 返回的是字节数不是字符数。处理中文时 "你好".len() 是 6——记住这个就不会再困惑。

示例

Vec 的增删读改与遍历:

fn main() {
    let mut scores = vec![88, 92];
    scores.push(79);
    scores.push(95);

    // get 安全访问
    assert_eq!(scores.get(1), Some(&92));
    assert_eq!(scores.get(99), None);

    // 可变遍历:每人加 5 分
    for s in &mut scores {
        *s += 5;
    }
    assert_eq!(scores, vec![93, 97, 84, 100]);

    let total: i32 = scores.iter().sum();
    let avg = total as f64 / scores.len() as f64;
    println!("平均分 {avg:.1}");
    assert_eq!(total, 374);
}

String 的构建、拼接与 UTF-8 真相:

fn main() {
    let mut s = String::from("你好");
    s.push_str(",Rust");
    s.push('!');
    assert_eq!(s, "你好,Rust!");

    // format! 拼接,两个原字符串都还能用
    let name = String::from("小林");
    let hello = format!("{s} 我是 {name}");
    println!("{hello}");
    assert!(hello.contains("小林"));

    // len() 是字节数;字符数要用 chars().count()
    assert_eq!("你好".len(), 6);
    assert_eq!("你好".chars().count(), 2);
}

常见坑

  • v[i] 越界 panic:下标来自外部输入时一律用 .get(i);确信界内才用下标,并把「确信」写成注释。
  • 遍历时增删元素:for x in &v { v.push(...) } 借用冲突编译不过;常见解法是先 collect 出要加的,再 extend。
  • 用 + 连一串字符串:s1 + &s2 + &s3 每步都移动所有权,又长又绕;format! 一次成型,原值全部保留。
  • 把 len() 当字符数:对中文/emoji 文本,len() 是字节数;要「多少个字」用 .chars().count(),要按字符处理就用 .chars() 迭代。

小结

Vec:vec! 建、push 增、get 安全读、&/&mut/值三种遍历。String:UTF-8 的 Vec,push_str 追加、format! 拼接、禁止下标取字符、len() 是字节数。下一章:HashMap 与集合家族。