本文章旨在记录 Rust 学习过程中的一些知识。

Cargo

其作用相当于 Rust 的项目管理器和语言编译器。

  1. cargo new project_name:创建名为project_name的项目,文件夹内包含源码文件夹src,编译产物target.gitignore文件,Cargo.lock文件和Cargo.toml文件。
    • src:内含main.rs初始源代码,将源代码存放此处。
    • target
      • debug:存放调试版本的编译产物。
      • release:存放发布版本的编译产物。
    • .gitignore:Git忽略文件。
    • Cargo.lock:锁定依赖版本的文件。
    • Cargo.toml:项目配置文件,包含项目名称、版本、依赖等信息。
  2. cargo build:编译项目,生成调试版本的可执行文件,存放在target/debug目录下。
    默认为--debug模式,可通过--release参数生成发布版本,存放在target/release目录下。
  3. cargo run:编译并运行项目,等同于先执行cargo build然后运行生成的可执行文件。
  4. cargo check:快速检查代码确保可以通过编译。

基础语法

  1. 基本数据类型:

    • 整数类型:

      类型名称 位数 范围
      i8 8 -2^7 ~ 2^7-1
      i16 16 -2^15 ~ 2^15-1
      i32 32 -2^31 ~ 2^31-1
      i64 64 -2^63 ~ 2^63-1
      i128 128 -2^127 ~ 2^127-1
      isize 与系统位数相同 -2^(n-1) ~ 2^(n-1)-1
      u8 8 0 ~ 2^8-1
      u16 16 0 ~ 2^16-1
      u32 32 0 ~ 2^32-1
      u64 64 0 ~ 2^64-1
      u128 128 0 ~ 2^128-1
      usize 与系统位数相同 0 ~ 2^n-1
    • 浮点数类型:f32(单精度浮点数),f64(双精度浮点数,默认类型)。

    • 布尔类型:bool,取值为truefalse

    • 字符类型:char,表示单个Unicode字符,占4字节。

    • 单元类型:(),表示没有值的类型,类似于其他语言中的void

  2. 复合数据类型:

    • 元组(Tuple):用来将多个不同类型的值组合在一起。
    • 数组(Array):用来存储多个相同类型的值,长度固定。
    • 切片(Slice):动态大小的视图,引用数组的一部分。
    • 结构体(Struct):自定义数据类型,可以包含多个字段。
  3. 常变量声明:

    1
    2
    3
    const a = 1;
    let b: i32 = 2;
    let mut c = 3;
    • 类型声明在常/变量名后面使用: type,整型默认i32,浮点型默认f64

    • 变量默认为不可变,如果需要变量可变,需要加mut

    • 如果等号右侧类型确定,则左侧类型可以自动推导。

    • 如果不同类型之间进行初始化,使用type1::from(small: type2),将 small 从 type1 类型转变为 type2 类型。

    • 遮蔽Shadowing

      1
      2
      let x: i32 = 1;
      let x: f64 = 1.1;

      对于一变量名,可重复使用,在这段代码之后使用该变量则为覆盖之后的变量。

  4. IO:

    1
    2
    3
    4
    5
    let mut n = String::new();
    std::io::stdin()
    .read_line(&mut n)
    .expect("Failed to read a line");
    let n: u32 = n.trim().parse().expect("expect a number");

    read_line函数读入一行,保存到 n 中,返回一个 Result 类型,使用 expect 函数处理。

    之后将 n 转换为u32类型,使用trim函数修剪,使用parse函数转换为想要的值,返回Result<F, F::Err>

  5. 语句和表达式:

    语句:执行操作但不返回值。如初始化/赋值语句

    表达式:计算并产生一个值。

  6. 带有返回值的函数:

    返回值使用return关键字,一般函数默认返回最后一个表达式的值,使用->后置函数返回类型。

    无返回类型函数返回单位类型(),类似于void

  7. 条件分支:
    if-else,可以不使用(),包括可以使用||&&,

  8. 循环:

    1
    2
    3
    loop {

    }
    1
    2
    for range {
    }
    1
    2
    while condition {
    }

    循环标签:使用'name': 为循环命名,使用continue/break name;即可跳出/退出指定循环。

    循环返回值:使用break value;可以让循环语块返回特定值。

  9. 所有权

    规则:

    • Rust 中的每一个值都有一个所有者(owner)。
    • 值在任一时刻有且只有一个所有者。
    • 当所有者离开作用域,这个值将被丢弃。

    浅拷贝和深拷贝:

    Rust 中永远默认浅拷贝。

    1. 对于含有存在于堆中的元素:一旦进行赋值语句,原值可以看作move到了新值中,原值不可再使用。
    2. 对于只存在于栈中的元素:浅拷贝和深拷贝无差异,赋值之后仍可以使用。

    如果需要深拷贝,可以使用.clone()成员函数。

    如果需要赋值后原变量仍可使用,可以实现Copy trait,如果一个类型中有一部分含有Drop trait,则不允许再添加Copy trait

    对于函数,函数传入参数函数返回值同样可以转移所有权。

    变量的所有权总是遵循相同的模式:将值赋给另一个变量时它会移动。当持有堆中数据值的变量离开作用域时,其值将通过drop被清理掉,除非数据被移动为另一个变量所有

  10. 引用与借用:

    引用类似于指针,但保证在其生命周期内指向一个特定类型的有效值。引用类型声明:&type

    引用只是单纯指向,而不是拥有

    在引用变量生命周期结束后,不会释放其指向的资源。

    创建一个引用的行为称为借用

    引用默认不可变,如需修改引用指向的变量,需声明可变引用&mut type

    在同一个可变引用的作用域内,不可以再创建另一个可变引用。(避免数据竞争)

    在不可变引用的作用域内,不可以再创建可变引用。

  11. Slice切片:

    Slice允许引用集合中的一个连续片段,而不引用整个集合。

    &set[begin..end],包含该集合中的[begin, end),begin默认为0,end默认为set length。

结构体Struct

  1. 定义方式:

    1
    2
    3
    4
    5
    6
    struct {
    name1: type1,
    name2: type2,
    ......

    }
  2. 创建实例: