语法基础

什么是 crate?

相当于类库。

crates.io 上有很多 Rust 社区成员发布的库。

创建和使用模块

通过执行 cargo new --lib restaurant,来创建一个新的名为 restaurant 的库。

创建项目

cargo new hello_cargo

构建项目

cargo build

构建并运行

cargo run

只检查不生成

cargo check

定义一个模块,使用 mod 关键字:


#![allow(unused)]
fn main() {
    mod front_of_house {
        mod hosting {
            fn add_to_waitlist() {}

            fn seat_at_table() {}
        }

        mod serving {
            fn take_order() {}

            fn server_order() {}

            fn take_payment() {}
        }
    }
}

名称的引用:

// Absolute path
crate::front_of_house::hosting::add_to_waitlist();

// Relative path
front_of_house::hosting::add_to_waitlist();

使用 pub 关键字公开名称。

super 关键字类似 CLI’s ..

fn fix_incorrect_order() {
    cook_order();
    super::serve_order();
}

结构体

pub struct Breakfast {
    pub toast: String,
    seasonal_fruit: String,
}

于其上的方法及结构体的实例化

impl Breakfast {
    pub fn summer(toast: &str) -> Breakfast {
        Breakfast {
            toast: String::from(toast),
            seasonal_fruit: String::from("peaches"),
        }
    }
}

use 关键字

将名称引入作用域

use crate::front_of_house::hosting;

as 关键字

类似于 typedef,别名

use std::io::Result as IoResult;

pub use:

  • 将名称引入作用域
  • 并同时使其可供其他代码引入自己的作用域

路径分配律

use std::{cmp::Ordering, io};
// 等价于
use std::cmp::Ordering;
use std::io;

枚举

enum SpreadsheetCell {
    Int(i32),
    Float(f64),
    Text(String),
}

let row = vec![
    SpreadsheetCell::Int(3),
    SpreadsheetCell::Text(String::from("blue")),
    SpreadsheetCell::Float(10.12),
];

API 基础

向量

let v: Vec<i32> = Vec::new();
let v = vec![1, 2, 3];
let mut v = Vec::new();

v.push(5);
let v = vec![1, 2, 3, 4, 5];

let third: &i32 = &v[2]; // or v.get(2);
let v = vec![100, 32, 57];
for i in &v {
    println!("{}", i);
}

字符串

let mut s = String::new();

let data = "initial contents";

let s = data.to_string();

let hello = String::from("السلام عليكم");



let mut s = String::from("foo");
s.push_str("bar");

let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // 注意 s1 被移动了,不能继续使用

散列表

use std::collections::HashMap;

let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);

Box

Rust 的 Box 类似 C++ 的 unique_ptr。二者都是单所有权智能指针,并且将数据放在堆中。区别在于 Box 不能为 null. c++ unique pointer VS rust ownership : rust

有关操作

分配

Box::new(Point {x: 1, y: 2});

解引用

boxed_point.deref();
// 不是 *boxed_point;!!

区别:pointers - Why is the return type of Deref::deref itself a reference? - Stack Overflow

销毁

drop(boxed_point);

Option

Option 是一个枚举

// 简化版
pub enum Option<T> {
    Some(T),
    None,
}

如何返回一个 Option:

// 不会 `panic!` 的整数除法。
fn checked_division(dividend: i32, divisor: i32) -> Option<i32> {
    if divisor == 0 {
        // 失败表示成 `None` 取值
        None
    } else {
        // 结果 Result 被包装到 `Some` 取值中
        Some(dividend / divisor)
    }
}

模式匹配

    match checked_division(dividend, divisor) {
        None => println!("{} / {} failed!", dividend, divisor),
        Some(quotient) => {
            println!("{} / {} = {}", dividend, divisor, quotient)
        },
    }

解包

optional_float.unwrap()

self、&self 和 &mut self

如果一个参数中有个 某 self

  • self:表示所有权转移到该方法中
  • &self 其实是 self: &Self 的简写,表示该方法是对此对象的不可变借用。类比 C++ 的 const &type name
  • mut &self 类比 C++ 的 &type name,解除了不可变的限制 Structs · A Guide to Porting C and C++ code to Rust

() 类型

相当于 C++ 的 void

std::mem::replace

作用如下:

upgit_20220421_1650511583.png

访问命令行参数

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    println!("{:?}", args);
}

环境变量

使用迭代器

  • 使用 in 迭代
fn main() {
    let v1 = vec![1,2,3,4];
    for val in v1.iter() {
        print!("{},", val)
    }
}
  • 判断是否是 last
fn main() {
    let v1 = vec![1, 2, 3, 4];
    for (i, val) in v1.iter().enumerate() {
        print!("{}", val);
        if i == v1.len() - 1 {
            print!("\n")
        } else {
            print!(",")
        }
    }
}
  • 迭代前跳过 1 个
fn main() {
    let v1 = vec![1, 2, 3, 4];
    for val in v1.iter().skip(1) {
        print!("{},", val)
    }
}
  • 迭代中跳过 1 个
fn main() {
    let v1 = vec![1, 2, 3, 4];
    for (i, val) in v1.iter().enumerate() {
        if i == 1 {
            continue;
        }
        print!("{},", val)
    }
}

文件

打印到 stderr

eprintln!("{}", "Error, world!");
use std::io::{self, Write};

    let mut s = String::new();
    io::stdin().read_line(&mut s).unwrap();
    io::stderr().write_all(s.as_bytes()).unwrap();

进程

休眠 1s

use std::thread;
use std::time::Duration;


fn main() {
    println!("睡啦!");
    thread::sleep(Duration::from_secs(1));
    println!("醒啦!")
}

转换

String to int

let my_int = my_string.parse::<i32>().unwrap();
let my_int: i32 = my_string.parse().unwrap();

整数间转换

let time: u64 = t as u64;

闭包

闭包是可以捕获环境的匿名函数

疑问:

  1. 捕获的是原来的变量,还是暂存到新的变量?
  2. 能否以某种方式修改原来的变量?

闭包可以通过三种方式捕获其环境,他们直接对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。这三种捕获值的方式被编码为如下三个 Fn trait:

  • FnOnce 消费从周围作用域捕获的变量,闭包周围的作用域被称为其 环境,environment。为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。其名称的 Once 部分代表了闭包不能多次获取相同变量的所有权的事实,所以它只能被调用一次。
  • FnMut 获取可变的借用值所以可以改变其环境
  • Fn 从其环境获取不可变的借用值

futures

前置知识:move

200 行代码讲透 Rust Futures :: steven 的个人博客