Rust Cheat Sheet

变量

可变与不可变

1
2
let a = 10; // 不可变
let mut b = 20; // 可变

数据类型

整型

1
let a: u64 = 10; 
长度 有符号 无符号
8bits i8 u8
16bits i16 u16
32bits i32 u32
64bits i64 u64
128bits i128 u128
与cpu架构相关 isize usize

数字时指定类型,见下例中的9u32

1
println!("9 / 2 = {} but 9.0 / 2.0 = {}", 9u32 / 2, 9.0 / 2.0);

浮点型

类型为:f32f64。浮点型默认为f64

1
2
let a: f32 = 10.0;  
let b = 11.0 // 默认f64

布尔型

类型为:bool;取值为:true and false

1
let b: bool = true;

字符与字符串

1
2
3
let c: char = 'f';  // 字符
let string_c: &str = "ace"; // 字符串切片
let string_s = String::from("hello"); // 字符串

这里的string_c为“字符串切片”

元组

定义和访问元组

1
2
3
4
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;

数组

1
2
3
4
5
let days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; 
// 声明一个全0数组,数据成员个数为5
let bytes = [0; 5];
// 使用数组成员
let first = days[0];

数组的两个重要特征:数组的每个元素都具有相同的数据类型。 数据类型永远不会更改。数组大小是固定的。 长度永远不会更改。

向量 Vector

与数组不同之处在于,向量的大小或长度可以随时增大或缩小。 在编译时,大小随时间更改的功能是隐式的。 因此,Rust 无法像在数组中阻止越界访问一样在向量中阻止访问无效位置。

1
2
3
4
5
6
7
8
9
10
11

let three_nums = vec![15, 3, 46];
println!("Initial vector: {:?}", three_nums);

let mut fruit = Vec::new();
fruit.push("Apple");
println!("Pop off: {:?}", fruit.pop());

// 索引
let mut index_vec = vec![15, 3, 46];
let three = index_vec[1];

哈希表

1
2
3
4
5
6
7
8
9
10
use std::collections::HashMap; 
//声明与插入元素
let mut reviews: HashMap<String, String> = HashMap::new();
reviews.insert(String::from("Ancient Roman History"), String::from("Very accurate."));
//获取键值
let book: &str = "Programming in Rust";
println!("\nReview for \'{}\': {:?}", book, reviews.get(book));

let obsolete: &str = "Ancient Roman History";
reviews.remove(obsolete);

结构体

定义结构体

1
2
3
4
// 经典结构
struct Student { name: String, level: u8, remote: bool }
// 元组结构
struct Grades(char, char, char, char, f32);

主要区别:经典结构中的每个字段都具有名称和数据类型。 元组结构中的字段没有名称。

实例化

1
2
3
4
5
6
7
8
let user_1 = Student { name: String::from("Constance Sharma"), remote: true, level: 2 };

// Instantiate tuple structs, pass values in same order as types defined
let mark_1 = Grades('A', 'A', 'B', 'A', 3.75);

println!("{}, level {}. Remote: {}. Grades: {}, {}, {}, {}. Average: {}",
user_1.name, user_1.level, user_1.remote, mark_1.0, mark_1.1, mark_1.2, mark_1.3, mark_1.4);

枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 声明
enum WebEvent {
WELoad,
WEKeys(String, char),
WEClick { x: i64, y: i64 }
}

// 或

struct KeyPress(String, char);
struct MouseClick { x: i64, y: i64 }
enum WebEvent { WELoad(bool), WEClick(MouseClick), WEKeys(KeyPress) }

// 赋值
let click = MouseClick { x: 100, y: 250 };
let keys = KeyPress(String::from("Ctrl+"), 'N');

let we_load = WebEvent::WELoad(true);
// Set the WEClick variant to use the data in the click struct
let we_click = WebEvent::WEClick(click);
// Set the WEKeys variant to use the data in the keys tuple
let we_key = WebEvent::WEKeys(keys);

泛型

1
2
3
4
5
6
7
8
9
struct Container<T> {
value: T,
}

impl<T> Container<T> {
pub fn new(value: T) -> Self {
Container { value }
}
}

函数

1
2
3
4
5
6
7
fn goodbye(message: &str) {
println!("\n{}", message);
}

fn divide_by_5(num: u32) -> u32 {
num / 5
}

所有权

所有权三原则

  1. Each value in Rust has a variable that’s called its owner. (Rust中每一个变量都有一个所有者。)
  2. There can only be one owner at a time.(在任一时刻,所有者有且仅有一个。)
  3. When the owner goes out of scope, the value will be dropped.(当所有者离开其作用域后,它所拥有的数据会被释放。)

Copy Trait

凡是拥有Copy trait的数据类型,“=”都表示数据的复制而非传有权的转移。以下数据类型有Copy trait:

  • 所有整型:u32 u64等
  • 布尔型
  • 所有浮点型:f32 f64等
  • 字符型:char
  • 元组(Tuples):如果组成元组的每个成员都有Copy trait,那么此元组也有Copy trait。

引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 引用
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}

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

// 可变引用
fn main() {
let mut s = String::from("hello");
change(&mut s);
}

fn change(some_string: &mut String) {
some_string.push_str(", world");
}

引用的原则

引用的原则:

  • 任何时刻,一个变量只能有
    • 一个可变引用,或者
    • 多个不可变引用
    • 以上两点不可同时存在
  • 引用应该总是合法的

Rust编译器在以下三个条件同时满足时,会产生数据竞争,发出编译错误:

  • 两个或两个以上的pointer(包含所有者,可变引用)指向同一份数据。
  • 其中至少一个可变引用指会向空间写入数据。
  • 没有同步数据的访问机制。

手动批注生存期(lifetime annotation)

1
2
3
4
5
6
7
fn longest_word<'a>(x: &'a String, y: &'a String) -> &'a String {
if x.len() > y.len() {
x
} else {
y
}
}

以上代码中,x和y的生命周期有可能不一样,所以函数返回值的生命周期实际上是由两个参数里生命周期较短的那个决定的。

条件判断

loop

在断点处返回一个值

1
2
3
4
5
6
7
8
9
let mut counter = 1;
// stop_loop is set when loop stops
let stop_loop = loop {
counter *= 2;
if counter > 100 {
// Stop loop, return counter value
break counter;
}
};

for

1
2
3
4
5
6
7
8
9
let big_birds = ["ostrich", "peacock", "stork"];
for bird in big_birds.iter() {
println!("The {} is a big bird.", bird);
}

// 此代码遍历数字 0、1、2、3 和 4
for number in 0..5 {
println!("{}", number * 2);
}

while

1
2
3
4
while counter < 5 {
println!("We loop a while...");
counter = counter + 1;
}

Option与Result 枚举

原型

1
2
3
4
5
6
7
8
9
enum Option<T> {
None, // The value doesn't exist
Some(T), // The value exists
}

enum Result<T, E> {
Ok(T): // A value T was obtained.
Err(E): // An error of type E was encountered instead.
}

Result

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
33
34
35
36
37
38
39
#[derive(Debug)]
struct DivisionByZeroError;

fn safe_division(dividend: f64, divisor: f64) -> Result<f64, DivisionByZeroError> {
if divisor == 0.0 {
Err(DivisionByZeroError)
} else {
Ok(dividend / divisor)
}
}


fn read_file_contents(path: PathBuf) -> Result<String, Error> {
let mut string = String::new();

// Access a file at a specified path
// ---------------------------------
// - Pass variable to `file` variable on success, or
// - Return from function early if there's an error
let mut file: File = match File::open(path) {
// Corrected code: Pass variable to `file` variable on success
Ok(file_handle) => file_handle,
// Corrected code: Return from function early if there's an error
Err(io_error) => return Err(io_error),
};

// Read file contents into `String` variable with `read_to_string`
// ---------------------------------
// Success path is already filled in
// Return from the function early if it is an error
match file.read_to_string(&mut string) {
Ok(_) => (),
// Corrected code: Return from function early if there's an error
Err(io_error) => return Err(io_error),
};

// Corrected code: Return `string` variable as expected by function signature
Ok(string)
}

match

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// vec!与match
let fruits = vec!["banana", "apple", "coconut", "orange", "strawberry"];
for &index in [0, 2, 99].iter() {
match fruits.get(index) {
Some(fruit_name) => println!("It's a delicious {}!", fruit_name),
None => println!("There is no fruit! :("),
}
}

// 仅在Option为某个值时执行print
let a_number: Option<u8> = Some(7);
match a_number {
Some(7) => println!("That's my lucky number!"),
_ => {},
}

// if let

let a_number: Option<u8> = Some(7);
if let Some(7) = a_number {
println!("That's my lucky number!");
}

Trait

Trait是一组类型可实现的通用接口。个人理解:类似于给类定义方法。

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
trait Area {
fn area(&self) -> f64;
}

struct Circle {
radius: f64,
}

struct Rectangle {
width: f64,
height: f64,
}

impl Area for Circle {
fn area(&self) -> f64 {
use std::f64::consts::PI;
PI * self.radius.powf(2.0)
}
}

impl Area for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}

可以编写一个函数,该函数接受任何实现 AsJson Trait的类型

1
2
3
4
5
fn send_data_as_json(value: &impl AsJson) {
println!("Sending JSON data to server...");
println!("-> {}", value.as_json());
println!("Done!\n");
}

迭代器

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
#[derive(Debug)]
struct Counter {
length: usize,
count: usize,
}

impl Counter {
fn new(length: usize) -> Counter {
Counter {
count: 0,
length,
}
}
}

impl Iterator for Counter {
type Item = usize;

fn next(&mut self) -> Option<Self::Item> {

self.count += 1;
if self.count <= self.length {
Some(self.count)
} else {
None
}
}
}

println

1
2
3
fn main() {
println!("The first letter of the English alphabet is {} and the last letter is {}.", 'A', 'Z');
}

derive(Debug)

通过#[derive(Debug)]语法可以在代码执行期间查看某些在标准输出中无法查看的值。 要使用 println! 宏查看调试数据,请使用语法 {:#?} 以可读的方式格式化数据。

1
2
3
4
5
#[derive(Debug)]
struct MouseClick { x: i64, y: i64 }

let m = MouseClick{ x: 20, y: 30};
println!("{:#?}", m);

todo

1
todo!("Display the message by using the println!() macro");

工具