准备开始学习一门新的语言Rust

语言规范

编译器

rustc

  1. 跨平台
  2. 支持交叉编译
  3. 使用LLVM作为编译器后端
  4. Rust开发
  5. 对Rust源码进行分析检查后翻译为LLVM IR
  6. 输出错误信息友好和详尽
$ rustc --version
rustc 1.48.0 (7eac88abb 2020-11-16)

LLVM

核心库

标准库的基础,不依赖于操作系统和网络等相关的库,不知道堆分配,也不提供并发与IO

标准库

技术跨平台支持

包管理器

包(crate)可理解为一个项目

第三方包(库)

$ cargo new bin_crate
$ cargo new --lib lib_crate

编译

$ cargo build
$ cargo b

$ cargo build --release
$ cargo b --release

编译并运行

$ cargo run
$ cargo r

语句与表达式

不可变绑定与可变绑定

let关键字用于绑定,绑定是建立标识符和值之间的关联关系。 比如变量和常量,其中变量的值是可变的对应可变绑定,而常量的值是不可变的对应于不可变绑定。

fn main() {
    let a1 = 1;
    a1 += 1; // 错误,因为a1是不可变绑定,所以在此试图对其值的修改是错误的。
    let mut a2 = 2;
    a2 += a1; // 正确,a2为可变绑定。

    println!("{}", a1);
    println!("{}", a2);
}

error[E0384]: cannot assign twice to immutable variable `a1`
 --> src/main.rs:3:5
  |
2 |     let a1 = 1;
  |         --
  |         |
  |         first assignment to `a1`
  |         help: make this binding mutable: `mut a1`
3 |     a1 += 1;
  |     ^^^^^^^ cannot assign twice to immutable variable

所有权与错用

fn print_type_of< T >(_: &T) {
    println!("{}", std::any::type_name::< T >())
}

fn main() {
    let s1 = "hello 1";
    let s2 = "hello 2".to_string();
    print_type_of(&s1);
    print_type_of(&s2);

    let o = s1;
    print_type_of(&o);
    println!("{:?}", s1);
    let o = s2;
    print_type_of(&o);
    println!("{:?}", s2);
}

error[E0382]: borrow of moved value: `s2`
  --> src/main.rs:16:22
   |
7  |     let s2 = "hello 2".to_string();
   |         -- move occurs because `s2` has type `String`, which does not implement the `Copy` trait
...
14 |     let o = s2;
   |             -- value moved here
15 |     print_type_of(&o);
16 |     println!("{:?}", s2);
   |                      ^^ value borrowed here after move

函数与闭包

作用域与生命周期

fn main() {
    let v = "hello world!";
    assert_eq!(v, "hello world!");
    let v = "hello Rust!"; // 对同一变量进行绑定的做法叫变量遮蔽,值以最新的绑定为准。
    assert_eq!(v, "hello Rust!");
    {
        let v = "Hello World!"; // 这个v变量的作用域和生命周期被限制在这个大扩号内。
        assert_eq!(v, "Hello World!");
    }
    assert_eq!(v, "hello Rust!");
}

函数指针

Rust语言中,函数被称为一等公民。这意味着,函数自身就可以作为函数的参数和返回值使用。

pub fn math(op: fn(i32, i32) -> i32, a: i32, b: i32) -> i32 {
    return op(a, b);
}

fn sum(a: i32, b: i32) -> i32 {
    return a + b;
}

fn product(a: i32, b: i32) -> i32 {
    return a * b;
}

fn main() {
    let a = 2;
    let b = 3;

    assert_eq!(math(sum, a, b), 5); // sum和product是被做为参数的函数
    assert_eq!(math(product, a, b), 6);
}

函数作为返回值的例子

fn is_true() -> bool { true } // 作为返回值的函数
fn true_maket() -> fn() -> bool { is_true } // 返回值是函数的函数
fn main() {
    assert_eq!(true_maket()(), true);
}

CTFE机制

编译时函数执行(Compile-Time Function Execution, CTFE)

const fn init_len() -> usize {
    return 9;
}

fn main() {
    let arr = [0; init_len()];
    // 通过[0; N]这种形式来初始化一个初始值为0,长度为N的数组。其中长度N必须在编译期知道值,否则编译出错。所以init_len必须在编译期求值。
    println!("{:?}", arr);
}

闭包

流程控制

条件表达式

fn main() {
    let n = 13;
    let big_n = if n < 10 && n > -10 {
        10 * n
    }
    else {
        n / 2
    };

    assert_eq!(big_n, 6);
}

循环表达式

Rust有三种循环表达式: while, loop 和 for…in

下面以Fizz Buzz问题实现举例

给你一个整数n. 从 1 到 n 按照下面的规则打印每个数: 如果这个数被3整除,打印fizz. 如果这个数被5整除,打印buzz. 如果这个数能同时被3和5整除,打印fizz buzz.

  • while
fn main() {
    let mut n = 1;
    while n < 101 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
        n += 1;
    }
}
  • loop
fn main() {
    let mut n = 1;
    loop {
        if n > 100 { break; }
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
        n += 1;
    }
}
  • for…in

n >= 1 && n < 101

fn main() {
    for n in 1..101 {
        if n % 13 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }
}
  • 无限循环

务必使用loop,避免使用while true。

match表达式与模式匹配

match类似于其它语言中的swatch或case。

fn main() {
    for number in 0..43 {
        match number {
            0 => println!("{} Origin", number),
            1..=3 => println!("{} All", number), // 区间
            | 5 | 7 | 13  => println!("{} Bad Luck", number), // 或
            n @ 42 => println!("Answer is {}", n), // @符可以将模式中的值绑定给一个变量
            _ => println!("{} Common", number), // default
        }
    }
}

match分支使用了模式匹配,分支左边是模式,右边是执行代码。

if let和while let表达式

用于某些场合下替代match表达式,左侧为模式,右侧为要匹配的值。

基本数据类型

布尔类型

bool = true / false

不支持将数字转换为bool类型

数字类型

u8,u16,u32,u64,u128

i8, i16, i32, i64, i128

usize, isize

f32, f64

字符类型

使用单引号来定义字符。

数组类型

范围类型

切片类型

str字符串类型

原生指针

never类型

复合数据类型

元组 tuple

结构 struct

枚举 enum

联合 union

常用集合类型

线性序列:向量

线性序列:双端队列

线性序列:链表

key-value映射表:HashMap和BTreeMap

集合:HashSet和BTreeSet

优先列表:BinaryHeap

智能指针

泛型和trait