• 使用rustlings学学rust
  • 由于我要考雅思,于是就用英文写了,补救补救我的英语

    EX

EX01

  • change the printline!to println!

the println! can print sth with \n,but print! can't

EX02

  • add let in front of the x

1.the variable declare by the let is read-only except of adding mut
2.let can automatically infer the type of the variable

EX03

  • 好吧,我博客的富文本显示器甚至不支持rust的代码块

    fn main() {
      // TODO: Change the line below to fix the compiler error.
      let x;
    
      if x == 10 {
          println!("x is ten!");
      } else {
          println!("x is not ten!");
      }
    }
    
  • assign inital value to x

1.You absolutely can't read or use a variable before it's actually assigned.
2.But you can first declare a variable with let without giving it an initial value.
3.If you violate the principle above, the compiler will just throw an error and refuse to compile.
4.Because rust won’t assign a default value or random garbage memory to uninitialized variables

EX04

  • i32 means int 32, 32-bit integer, so I need to assign a corresponding value to the variable

EX05

  • add mut to make the variable variable (

EX06

fn main() {
    let number = "T-H-R-E-E"; // Don't change this line
    println!("Spell a number: {number}");

    // TODO: Fix the compiler error by changing the line below without renaming the variable.
    number = 3;
    println!("Number plus two is: {}", number + 2);
}
  • add let to create a brand new variable, that will shadow the former variable

Even if I add mut to line 1, that still make no sense, because you can't change the variable from one
type to another type, like from string to int

EX05

// TODO: Change the line below to fix the compiler error.
const NUMBER = 3;

fn main() {
    println!("Number: {NUMBER}");
}
  • add : i32 behind the NUMBER

using const to declare variable do not support the "type inference", so you must declare the type manually

EX06

  • just add a function named "call_me"

EX07

  • Well, leak of the type declaration, so just add : i32

EX08

num: u8:unsigned 8-bit integer

EX10

  • In a function, the last line without the end of ; is a Expression instead of a Statement, so it can act as return, meaning that the result of the Expression will be return, of course you can also use return like C

EX if3

  • All branch of the Expression of if-else need a equal data type.
  • Not like Python, Rust absolutely won’t secretly convert integers to floats at runtime

EX structs1

  • Just add the field of the struct to test and instantiate the struct.
  • There are three type of the struct, consist of regular, tuple, unit.

EX structs2

  • Need to use struct upadte syntax to instantiate the new struct.

&str is not same as String.
&str is a slice of string, which was hard-code in the binary file.
String is allocated to the heap, String::from("...") or "...".to_string() can be used to transfer the &str to String

EX structs3

  • Finish the logic part of the function.

EX enums

  • The enum of the Rust have a far cry from Python (

Knowledge

array

  • create an array
fn main() {
    // Rust 会自动推导 a 的类型为 [i32; 5]
    let a = [1, 2, 3, 4, 5];
    
    // Rust 自动推导 months 为 [&str; 12]
    let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
}

fn main() {
    // 强制声明这是一个包含 5 个 32位整数的数组
    let a: [i32; 5] = [1, 2, 3, 4, 5];
    
    // 强制声明这是一个包含 3 个 无符号8位整数的数组
    let bytes: [u8; 3] = [255, 0, 128];
}

fn main() {
    // 创建一个包含 5 个 3 的数组。等价于 [3, 3, 3, 3, 3]
    let a = [3; 5]; 
    
    // 创建一个大小为 1024,内部全为 0 的缓冲区,这在处理底层字节流时非常常见
    let buffer: [u8; 1024] = [0; 1024]; 
}

// 创建一个动态数组,可以随时向里面 push 新元素
let mut vec_a = vec![1, 2, 3];
vec_a.push(4);
  • vec is allocated in heap, while others are in stack.

slice

let a = [10, 20, 30, 40, 50];

// 1. 省略起点(默认从 0 开始)
let slice1 = &a[..2];    // 等价于 &a[0..2],输出: [10, 20]

// 2. 省略终点(默认直到数组末尾)
let slice2 = &a[3..];    // 包含索引 3 及之后所有元素,输出: [40, 50]

// 3. 全切片(引用整个数组)
let slice3 = &a[..];     // 输出: [10, 20, 30, 40, 50]

// 4. 包含终点(使用 ..=)
// 如果你希望左右两边都包含,加上等号
let slice4 = &a[1..=3];  // 包含索引 1, 2, 3,输出: [20, 30, 40]
  • Includes the start index but not the end index
  • the argument in [] means the index

切片操作会带来一个类型变化:
let a = [1, 2, 3, 4, 5];:

    ·a 的类型是 [i32; 5] (一个长度固定的数组,拥有这段内存的所有权)。

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

    ·slice 的类型变成了 &[i32] (一个整数切片)。

&[i32] :
它在底层是一个“胖指针(Fat Pointer)”。它本身不存储数据,而是包含了两个信息:

    ·指针:指向切片起始位置(即 a 数组中索引为 1 的位置)。
    ·长度:这个切片包含几个元素(长度为 2)。

切片只是借用了原数组的数据,没有产生新的所有权,所以它的开销极小。这也意味着,只要 slice 还在被使用,Rust 的借用检查器就会保护原数组 a,不允许原数组被销毁

Tuple Destructuring (元组解构)

let cat = ("Furry McFurson", 3.5);
let (name, age) = cat;

访问元组元素

  • Can't use [],[]is exclusively for array and slice
  • Join the name of the Tuple and the index using .
let numbers = (1, 2, 3);
let second = numbers.1;

vec 动态数组

  • Initialization:
// 方式 A:使用宏,直接给出一组初始值
let mut v1 = vec![1, 2, 3];

// 方式 B:使用宏,批量初始化相同的值 (比如创建 5 个 0)
let mut v2 = vec![0; 5]; 

// 方式 C:创建一个完全空的 Vec,不使用宏
// 注意:如果后面没有立刻 push 数据,编译器猜不出类型,需要显式标注
let mut v3: Vec<i32> = Vec::new();
  • Add:
let mut v = vec![1, 2];

// 尾部追加 (最常用,效率最高,O(1) 复杂度)
v.push(3); // 此时 v 变成 [1, 2, 3]

// 插入到指定索引 (原位置及后面的元素会自动向后移动,O(n) 复杂度)
v.insert(1, 99); // 在索引 1 的位置插入 99。此时 v 变成 [1, 99, 2, 3]
  • Remove:
let mut v = vec![10, 20, 30, 40];

// 弹出尾部元素 (最常用,O(1) 复杂度)
// 注意:它返回的不是具体的数字,而是一个 Option 枚举 (Some 里面包裹着值,或者 None 代表空)
let last_item = v.pop(); // last_item 是 Some(40),v 变成 [10, 20, 30]

// 移除指定索引的元素 (后面的元素会自动向前填补,O(n) 复杂度)
let removed_item = v.remove(1); // 移除索引 1 的元素。removed_item 是 20,v 变成 [10, 30]

迭代器适配器配合闭包

fn vec_map_example(input: &[i32]) -> Vec<i32> {
    // An example of collecting a vector after mapping.
    // We map each element of the `input` slice to its value plus 1.
    // If the input is `[1, 2, 3]`, the output is `[2, 3, 4]`.
    input.iter().map(|element| element + 1).collect()
}
  • input.iter() converts the slice [1, 2, 3] into an iterator..map can receive the element from the iterator one by one, then |element| element + 1 is 闭包, like the 匿名函数,.|element| define the input argument of this 匿名函数, element + 1 is the body of the function, and it's also its return value (note that there's no ; here; this is an expression).Iterators themselves are 'lazy' (they just represent a computation process). You need an action to collect all the finished products at the end of the pipeline and assemble them into a whole new data structure.So that is what thing the .collect() do.
    .collect() will see the -> Vec<i32>, and that decide that .collect() will return a Vec

ownership

  • 好吧,讲原理的问题还是用中文好点。
  • 分配在栈(Stack)上的数据,Rust 采取的是拷贝语义(Copy Semantics),即不会发生所有权转交的问题。
  • 分配在堆(heap)上的数据,Rust 采取的是移动语义(Move Semantics),即会发生所有权转交的问题
  • 所有权问题:
    // TODO: Make both vectors `vec0` and `vec1` accessible at the same time to
    // fix the compiler error in the test.
    #[test]
    fn move_semantics2() {
        let vec0 = vec![22, 44, 66];
        let vec1 = fill_vec(vec0);
        assert_eq!(vec0, [22, 44, 66]);
        assert_eq!(vec1, [22, 44, 66, 88]);
    }
  • Complex data structures like Vec, which are allocated on the heap, can only have one owner at a time.
    When you write let vec1 = fill_vec(vec0); something very important happens: you move the ownership of vec0 to the fill_vec function. Once the move is complete, the original owner vec0 immediately becomes invalid (to prevent Double Free)

2026-08-07T01:34:56.png

  • 好吧,其他的东西我就记在本地了,不放上来了喵(