Cheatsheet

// take 相当于
mem::replace(&mut option, None)
// map(fn) 相当于
match option {
    None => None,
    Some(x) => Some(fn(x))
}
// and_then(fn) 相当于
match option {
    None => None,
    Some(x) => fn(x)
}
// unwrap_or(default) 相当于
match option {
    None => default,
    Some(x) => x
}
// ok() 相当于
match result {
    Ok(x) => x,
    Err => None
}

fn main() {
    let array = [1,2,3];

    // 编译错误,array 没有实现 Iterator 特性
    // for i in array {
    //     println!("{}", i);
    // }
    for i in &array {
        println!("{}", i);
    }

    println!("{:?}", array.iter().map(|&x| x * 2).collect::<Vec<_>>());
    // https://doc.rust-lang.org/std/primitive.array.html
    // IntoIterator (implemented for &[T; N] and &mut [T; N])
    // 由于只对数组引用实现了 IntoIterator,在调用 into_iter 时自动将 array 转换成 &array
    println!("{:?}", array.into_iter().map(|&x| x * 2).collect::<Vec<_>>());

    let vector = vec![1,2,3];

    // 向量内元素被移出,无法再次使用
    // for i in vector {
    //     println!("{}", i);
    // }
    for i in &vector {
        println!("{}", i);
    }

    // https://doc.rust-lang.org/std/iter/#the-three-forms-of-iteration
    // iter 遍历 &T,iter_mut 遍历 &mut T, into_iter 遍历 T
    // 关键:iter 不移出元素,into_iter 移出元素
    println!("{:?}", vector.iter().map(|&x| x * 2).collect::<Vec<_>>());
    // into_iter 之后 vector 无法再使用,此处 x 为值而非引用
    println!("{:?}", vector.into_iter().map(|x| x * 2).collect::<Vec<_>>());
}

基本概念

基本类型

// 元组
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
// 数组
let a: [i32; 5] = [1, 2, 3, 4, 5];
let first = a[0];

控制语句

// 条件语句
let number = if condition { 5 } else { 6 };
// 循环语句
// 1. loop
let mut counter = 0;
let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2;
    }
};
// 2. while
let mut number = 5;
while number != 0 {
    println!("{}!", number);
    number = number - 1;
}
// 3. for
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
    println!("the value is: {}", element);
}
for number in (1..4).rev() {
    println!("{}!", number);
}

结构体

struct Point<T> {
    x: T,
    y: T,
}

// 结构体泛型方法
impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

// 结构体特定类型方法
impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

// 打印结构体
##[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    // {} 默认使用 Display 输出格式,{:?} 使用 Debug 输出格式
    println!("rect1 is {:?}", rect1);
}

枚举类

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

// 定义枚举类方法
impl Message {
    fn call(&self) {
        // 在这里定义方法体
    }
}

模式匹配

let some_u8_value = Some(0u8);
match some_u8_value {
    Some(3) => println!("three"),
    _ => (),
}
// 语法糖
if let Some(3) = some_u8_value {
    println!("three");
}

通用类型

向量

// 新建向量
let v: Vec<i32> = Vec::new(); // 空向量
let v = vec![1, 2, 3]; // 利用宏
let v = vec![String::new(); 5]; // 设置元素个数
let mut v = Vec::new(); // 更新元素
v.push(5);

// 获取向量元素
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2]; // 使用索引,超出范围引发 panic
match v.get(2) { // 使用get方法,超出范围返回 None
    Some(third) => println!("The third element is {}", third),
    None => println!("There is no third element."),
}

// 遍历向量元素
let v = vec![100, 32, 57];
for i in &v {
    println!("{}", i);
}

let mut v = vec![100, 32, 57];
for i in &mut v {
    *i += 50; // 解引用运算符(*)
}

字符串

// 新建字符串
let s = String::from("initial contents");
let s = "initial contents".to_string();

// 更新字符串
let mut s = String::from("foo");
s.push_str("bar");
s.push('l');

// 拼接字符串
// +运算符
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // 注意 s1 被移动了,不能继续使用
// format!宏
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);

// 遍历字符串
for c in "नमस्ते".chars() {
    println!("{}", c);
}

字符串不支持索引!

哈希表(字典)

use std::collections::HashMap;

// 新建哈希表
// 1. 用 insert 方法
let mut scores = HashMap::new();
let team = String::from("Blue");
let score = 10;
scores.insert(team, score);
// 一旦键值对被插入后就为哈希表所有,以下 team, score 不再有效

// 2. 用 collect 方法
let teams  = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
// 用下划线占位,rust能够推断出类型
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();

// 访问哈希表中的值
let team_name = String::from("Blue");
// 1. 通过键值访问
let score = scores[&team_name]  // 键值不存在引发 panic
let score = scores.get(&team_name); // 键值不存在返回 None
// 2. 遍历
for (key, value) in &scores {
    println!("{}: {}", key, value);
}

// 更新哈希表
// 覆盖值
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25);
// 只在没有键时插入
scores.entry(String::from("Blue")).or_insert(50);

错误处理

匹配错误

use std::fs::File;
use std::io::ErrorKind;

fn main() {
    // 直接使用模式匹配
    let f = File::open("hello.txt");
    let f = match f {
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("Tried to create file but there was a problem: {:?}", e),
            },
            other_error => panic!("There was a problem opening the file: {:?}", other_error),
        },
    };
    // 简写
    let f = File::open("hello.txt").map_err(|error| {
        if error.kind() == ErrorKind::NotFound {
            // 用 unwrap_or_else 处理错误
            File::create("hello.txt").unwrap_or_else(|error| {
                panic!("Tried to create file but there was a problem: {:?}", error);
            })
            // 或者用 if let 语句处理
            if let Err(e) = File::create("hello.txt") {
                panic!("Tried to create file but there was a problem: {:?}", e);
            }
        } else {
            panic!("There was a problem opening the file: {:?}", error);
        }
    });
}

// 简写失败时的panic
```rust
// unwrap 使用默认 panic! 信息
let f = File::open("hello.txt").unwrap();
// expect 手动设定错误信息
let f = File::open("hello.txt").expect("Failed to open hello.txt");

传播错误

// ? 只能用于返回 Result 的方法
// ? 将错误传递给 from 方法(定义于 From trait 中),from 方法负责将错误转换为当前方法返回值中定义的错误类型
fn read_username_from_file() -> Result<String, io::Error> {
    // 示例
    let mut f = File::open("hello.txt")?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
    // 简写
    let mut s = String::new();
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
    // 更简
    fs::read_to_string("hello.txt")
}

模块系统

定义模块

// 所有项默认私有,不能使用定义于当前模块的子模块中的私有代码,除非用pub公有化
mod sound {
    pub mod instrument {
        pub fn clarinet() {
            // 函数体
        }
    }
}

对结构体和枚举使用pub

// 公有结构体的成员仍是私有,必须手动公开
pub struct Vegetable {
    pub name: String,
    id: i32,
}
// 公有枚举所有成员都公有
pub enum Appetizer {
    Soup,
    Salad,
}

相对路径与绝对路径

// main 与 sound 同级别
fn main() {
    // 绝对路径
    crate::sound::instrument::clarinet();
    // 相对路径
    sound::instrument::clarinet();
}

// 使用 super 开始相对路径
mod instrument {
    fn clarinet() {
        // 类似于文件系统中的..
        super::breathe_in();
    }
}

fn breathe_in() {
    // 函数体
}

引入作用域

use std::fmt::Result;
use std::io::Result as IoResult;
// 嵌套路径
use std::{cmp::Ordering, io};
use std::fs::{self, File};
// glob 运算符
use std::collections::*;

特性

定义特性

pub trait Summary {
    fn summarize(&self) -> String;
}

定义带默认实现的特性

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")
    }
}
// 带默认实现的方法可以调用未实现方法
pub trait Summary {
    fn summarize_author(&self) -> String;

    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
    }
}

为结构体实现特性

pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

特性约束(trait bound)

pub fn notify<T: Summary>(item: T) { }
pub fn notify<T: Summary + Display>(item: T) { }
// 语法糖
pub fn notify(item: impl Summary) { }
pub fn notify(item: impl Summary + Display) { }

多个特性约束(trait bound)

fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 { }
// 语法糖
fn some_function<T, U>(t: T, u: U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{ }

生命周期

// 返回值的生命周期和x,y之间生命周期更短的那个一样
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
// 指定结构体的实例的生命周期不能长于part字段中的引用
struct ImportantExcerpt<'a> {
    part: &'a str,
}

impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part
    }
}
// 静态生命周期,存活于整个程序期间
// 字符串字面值的类型是 &'static str
let s: &'static str = "I have a static lifetime.";

测试

单元测试

// 单元测试混杂在源代码中,通过 cfg(test) 告诉编译器在编译时不需要把他们包含进编译结果中
##[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

集成测试

新建 tests 文件夹,在其中添加测试文件如 tests/integration_test.rs

use crate_name;

// 由于集成测试与源代码分离,不需要 cfg(test) 标注
##[test]
fn it_adds_two() {
    assert_eq!(4, adder::add_two(2));
}

如果多个集成测试需要共用某些方法,可以创建 tests/common/mod.rs 而非 tests/common.rs,这样编译器会把它当作模块而不是测试文件

命令行程序

// 读取命令行参数
use std::env;
let args: Vec<String> = env::args().collect();

// 检查环境变量
// env::var 方法返回 Result,is_err 判断返回值是否为 Err
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();

// 将错误打印到标准错误
eprintln!("Application error: {}", e);