Rust入门笔记(二)

记录在https://course.rs 上学习Rust中的一些知识点

2.4.1 字符串与切片

Rust的字符串和切片概念也是比较难的一点。感觉我之前几次都是看到这里,然后卡住了很久。后面就忙了没空继续看了,下一次又从头看起(捂脸)。所以这部分对我也是一座大山。

  1. String是字符串类型,内容是字节数组。值的内容在堆上,而本身是一个胖指针,包含ptr,len(已使用长度),capacity(最大容量)。
    len和capacity全是按字节数计算的,本身内容是utf-8编码(1~4个字节不定长),区别于char类型的Unicode固定4字节。所以不可以按索引遍历,避免遍历到某个字符中间的某个字节上

  2. 切片:引用集合(详细定义在后面几节)中部分连续的元素序列。字符串切片(&str类型),即引用字符串中的一部分, 索引是左闭右开。

    1
    2
    3
    4
    let s = String::from("hello world");

    let hello = &s[0..5];
    let world = &s[6..11];

    特殊索引:省略左边便是从0开始,省略右边表示直到最后。两侧全省略,即..表示全部

    由于第1点提过字符串索引是按字节的,所以通过所以去构造字符串切片需要谨慎。

  3. &str和String共享堆上的同一块内存,所有权在String, &str本身是引用,即借用

  4. 字符串字面量也是&str,例如let s: &str = "Hello, world!";

  5. &str转成String:

    1. String::from(“hello,world!”);`
    2. "hello,world".to_string()

    String转成&str:

    1. &s(&String可以被隐式转换成&str)
    2. &s[..](标准字符串切片语法)
    3. s.as_str()
  6. 遍历String:

    • 按Unicode字符遍历

      1
      2
      3
      for c in "中国人".chars() {
      println!("{}", c);
      }
    • 按字节遍历

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      for b in "中国人".bytes() {
      println!("{}", b);
      }

      //输出
      228
      184
      173
      229
      155
      189
      228
      186
      186

    如果想截取子串,需要借助三方库,例如utf8_slice

  7. 字符串操作:

    push、push_str操作原字符串

    insert操作原字符串

    replace、replacen不操作原字符串,replace_range操作原字符串

    pop、remove、truncate(删除指定位置到结尾的全部字符)、clear都是操作原字符串

    +、+=不操作原字符串,但是要求第二个参数一定是&str,不能是String(&String可以,会被强制转换)

2.4.2 元组+2.4.3结构体

  1. 元组是用()包起来的,可以使不同类型。可以通过“.+索引”来访问元组中指定的元素

  2. 结构体使用例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
    }

    let user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
    };
  3. 结构体被声明为可变时,才可修改里面字段的值

  4. 当结构体去某些字段的所有权时,结构体不可被访问。但是可访问里面仍有所有权的字段。
    例如失去了email的所有权,那么访问useruser.email都会报错,但是访问user.username是可以的

  5. 基于已有结构体创建新结构体,例如

    1
    2
    3
    4
    5
    let user2 = User {
    email: String::from("another@example.com"),
    ..user1
    };

    与ts用...来展开老对象不同的是,rust用..,且要把改变的值写在前面,..user1必须写在最后,这与ts是相反的

  6. 如果结构体里面的字段是引用类型(表明所有权借用自别处),则需要生命周期

  7. 想打印结构体,需要用#[derive(Debug)]标记结构体,然后用{:?}{:#?}

2.4.4 枚举

Rust的枚举也和其他语言很不一样,不只是几个固定的值,功能更加丰富。更像是一种分类,每个分类下还可以有更具体的值

  1. 最简单(仅有分类)的例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 定义
    enum PokerSuit {
    Clubs,
    Spades,
    Diamonds,
    Hearts,
    }

    // 使用
    PokerSuit::Clubs
  2. 每个分类下,带有不同的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 定义
    enum Message {
    Quit, // 不带值
    Write(String), // 带 1 个值
    Move(i32, i32), // 带多个值(元组)
    ChangeColor { r: u8, g: u8, b: u8 }, // 带命名字段(像 struct)
    }

    // 使用
    Message::Write(String::from("hello"));
    Message::Move(2,10);
    Message::ChangeColor{r: 10, g: 55, b: 38};
  3. 特殊的枚举Option,标准库内置的枚举类型,处理空值。有点类似ts中,将类型声明为T|undefined

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    enum Option<T> {
    Some(T),
    None,
    }

    // 使用
    let some_number = Some(5);
    let some_string = Some("a string");
    // 使用None值的时候,需要指定T的类型
    let absent_number: Option<i32> = None;

2.4.5 数组

Rust的数组(array)是定长的,和ts不一样,和c++很像。变长的是Vector

  1. 声明数组,类型声明中,元素类型和数量用分号分隔。元素类型必须一致

    1
    let a: [i32; 5] = [1, 2, 3, 4, 5];
  2. 快捷声明重复值

    1
    let a = [3; 5]; //[3, 3, 3, 3, 3]
  3. 通过索引访问数组元素a[0],如果索引越界会panic

  4. 如果数组元素不是基础类型(没有实现Copy特征),就不能使用上面的快捷创建重复值的数组写法
    因为这种写法是通过Copy来赋值的。可以用下面的std::array::from_fn方法,_i是未使用到的变量(类似匿名函数),值是当前的索引

    1
    2
    3
    let arr = [String::from("rust is good!"); 8]; // ❌️

    let arr: [String; 8] = std::array::from_fn(|_i| String::from("rust is good!")) // ✅️
  5. 数组切片
    我们常说的数组切片,其实指的是数组切片的引用。严格来讲,数组类型是[T; n],切片类型是[T],切片引用类型是&[T]。
    切片类型的长度是不固定的,因为需要真实包含被切的元素,但是具体几个又无法在编译期得知。
    但是切片引用类型的长度是固定的,它只需要存原本数组中,第一个切的元素的地址,和一共要包含多少个元素

    1
    2
    3
    4
    5
    let a: [i32; 5] = [1, 2, 3, 4, 5];

    let slice: &[i32] = &a[1..3];

    assert_eq!(slice, &[2, 3]);

Rust入门笔记(二)
https://miku03090831.github.io/2026/01/12/Rust入门笔记(二)/
作者
qh_meng
发布于
2026年1月12日
许可协议