Rust

Chapter2

let 定义变量,默认不可变,可变变量需要用 mut 关键字。

1
let mut num = 10;

强类型推导,会根据等号右边结果或者后文自动推导变量类型。使用冒号可以定义类型。Rust 在编译的时候就必须知道变量的类型。

1
2
let num: i32 = 10;
let num: u32 = 10;

println!用于输出,{}用于填入此参数,在大括号中填数字可以分组。

1
println!("cout {0}, {0}, {1}", a, b);

有些函数的返回值为Rusult,有Ok, Err两种,使用expect函数可以在错误时输出信息,但是程序仍然会崩溃。使用match可以优美(网上这么说的)的在错误时采取其他的行为从而使程序继续。

1
2
3
4
5
6
7
8
// expect
io::stdin().read_line(&mut num).expect("Failed to read line");

// match
match io::stdin().read_line(&mut num) {
Ok(num) => num,
Err(_) => ..., // _ = not care
};

猜数游戏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use rand::Rng;
use std::cmp::Ordering;
use std::io;

fn main() {
let secret_number = rand::thread_rng().gen_range(1..=100);

loop {
println!("Please input your guess.");

let mut guess = String::new();

io::stdin().read_line(&mut guess).expect("Failed to read line");

// shadow
// trim : delete " "
// parse : stoi
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue, // _ = not care
};

match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
};
}

Chapter3

常量不等于不可变的变量,const 声明,必须指定类型。

1
const MAX_NUM: i64 = 20070206;

Shadow 机制,同名变量后者会隐藏前者。

1
2
3
4
5
6
7
// mut
let mut num1 = 10;
num1 = 20;

// shadow
let mut num1 = 10;
let num1 = "dmx20070206";

Rust 的整数类型:

位长度 有符号 无符号
8 bit i8 u8
16 bit i16 u16
32 bit i32 u32
64 bit i64 u64
128 bit i128 u128
arch isize usize
不同进制:0x, 0o, 0b。任何进制都可以用 _ 分割。i32 为默认类型。

Rust 的浮点数类型:
f32, f64, IEEE-754 标准。f64 为默认类型。

Rust 的布尔类型:略

Rust 的字符类型:
表示一个 Unicode 字符,占用 4 个字节。

1
let num1 = '😅';

Rust 中的 Tuple:

1
2
let num: (u32, char, f32) = (10, '😅', 1.5);
println!("{}, {}, {}", num.0, num.1, num.2);

Rust 中的 数组:

1
2
3
4
5
6
7
// 类型; 长度
let num: [f64; 3] = [1.1, 1.2, 1.3];
println!("{}, {}, {}", num[0], num[1], num[2]);

// 数据; 重复次数
let num = [3; 5];
println!("{}, {}, {}, {}, {}", num[0], num[1], num[2], num[3], num[4]);

Rust 中的表达式类型:

1
2
3
4
5
// x = the last expression
let x = {
let num = 10;
num
};

Rust 中的函数,用fn关键字,参数必须声明类型,返回值用->符号定义,函数默认返回最后一个表达式,可以隐藏return

1
2
3
4
5
6
7
8
// return the last expression
fn function(x: u32) -> u32 {
x + 1
}

fn function(x: u32) -> u32 {
return x + 1;
}

Rust 中的 if-else:

1
2
let condition = true;
let num = if condition { 10 } else { 20 };

Chapter 4

所有权是 Rust 中很重要的概念,所有程序在运行时都必须管理它们使用计算机内存的方式。

  1. 有些语言自带 GC,程序运行时会一直搜索已经不用的内存
  2. 有些语言需要程序员显式的申请和释放内存
  3. Rust 通过一个所有权系统来管理内存,其中包含一组编译器在编译时检测的规则

Rust 中,可以向 Stack 和 Heap 申请内存。Stack 只能存放定长的数据,Heap 可以存放变长的数据。很明显,计算机访问 Stack 上的数据快于 Heap,因为从 Stack 获取数据一定是栈顶,并且 Stack 中各个数据离得近。

String 类型是典型的变长数据类型,它只能存放于 Heap 上。通过from方法可以通过字符串常量创建一个 String 类型,它存放于 Heap 上,所以它是可变的。

Drop, Move, Copy, Clone

Drop 清理规则:

  1. 当一个数据离开作用域时,立即释放它占用的内存
  2. 对于失效(Moved)的变量,不会释放它占用的内存
  3. 对于 Stack 上的简单数据,不需要 Drop
  4. 对于 Heap 上的数据,需要 Drop

Move 移动规则:

  1. 对于 Stack 数据,等同于 Copy
  2. 对于 Heap 数据,前者将被失效
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let num1 = 10;
    let num2 = num1; // copy

    let s1 = String::from("dmx");
    let s2 = s1; // s1 moved

    // borrow of moved value: `s1`
    // println!("{}", s1);

    // exit, s2 will be destoryed

Copy 拷贝规则:

  1. 如果变量一部分创建了 Drop,那么该变量不能拥有 Copy
  2. Copy 类型的变量一般存放于 Stack 上,不需要额外的 Drop 操作
    1
    2
    let num1 = 10;
    let num2 = num1; // copy type

Clone 克隆规则:

  1. 使用 clone 方法,可以实现深拷贝?
  2. 在 Heap 上开辟新的一块内存
    1
    2
    let s1 = String::from("dmx");
    let s2 = s1.clone(); // s1 still valid

函数的参数传递中所有权的转移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
fn main() {
let s = String::from("hello");
// s 被声明有效

takes_ownership(s);
// s 的值被当作参数传入函数
// 所以可以当作 s 已经被移动,从这里开始已经无效

let x = 5;
// x 被声明有效

makes_copy(x);
// x 的值被当作参数传入函数
// 但 x 是基本类型,依然有效
// 在这里依然可以使用 x 却不能使用 s

} // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放


fn takes_ownership(some_string: String) {
// 一个 String 参数 some_string 传入,有效
println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放

fn makes_copy(some_integer: i32) {
// 一个 i32 参数 some_integer 传入,有效
println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
let s1 = gives_ownership();
// gives_ownership 移动它的返回值到 s1

let s2 = String::from("hello");
// s2 被声明有效

let s3 = takes_and_gives_back(s2);
// s2 被当作参数移动, s3 获得返回值所有权
} // s3 无效被释放, s2 被移动, s1 无效被释放.

fn gives_ownership() -> String {
let some_string = String::from("hello");
// some_string 被声明有效

return some_string;
// some_string 被当作返回值移动出函数
}

fn takes_and_gives_back(a_string: String) -> String {
// a_string 被声明有效

a_string // a_string 被当作返回值移出函数
}

Rust 中的引用租借

引用是一种“借”所有权的过程,例如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let s1 = String::from("hello");

let len = calculate_length(&s1);

// s1 is borrowed
println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
s.len()
}

借用后不可以修改原数据,除非用mut关键字修饰,如下:

1
2
3
4
5
6
7
8
fn main() {
let mut s1 = String::from("dmx");

let s2 = &mut s1;

s2.push_str("20070206");
println!("{}", s2);
}

一个变量的某时刻的全部有效引用必须满足如下规则:

  1. 有至多一个可变引用
  2. 有若干个不可变引用
  3. 引用必须一致有效

同时,在 Rust 中不允许悬空引用,如下:

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let reference_to_nothing = dangle();
// null
}

fn dangle() -> &String {
let s = String::from("hello");

&s
}
// s will be destoryed

Rust
https://dmx20070206.github.io/2024/11/24/Rust/
Author
DM-X~X~X
Posted on
November 24, 2024
Licensed under