Rust是Mozilla公司发起的一个开源项目。它注重安全、性能和并发,是一种系统级编程语言。Rust是针对多核体系提出的语言,并且吸收一些其他动态语言的重要特性,比如不需要管理内存,比如不会出现Null指针等等。相比C/C++这些底层语言,rust不但具备底层控制力,且具有良好的高级语言特性,生态系统。Rust一经诞生就被运用于servo浏览器内核项目开发,具备实战经历。

首先,在学习rust之前要有这样的预期:rust不是python,不要期望一个星期迅速掌握。
然而,掌握之后好比光剑在手,可以尽情释放原力。

近期学习rust,是因为rust是支持webassembly的第二个语言,且性能强悍。

安装 & 升级

安装:curl https://sh.rustup.rs -sSf | sh
升级:rustup update

简单类型

rust支持这些整型数值:i32 u32 i8 u8 i16 u16 isize usize
浮点类型:f32 f64
其他:char, bool

变量声明如下。类型非必须,很多情况下编译器可以判断。

let a = 1;
let a = 1_f32;
let a: f32 = 1.0;

字符串

rust字符串都是合法的utf-8编码字符。

let s = "Rust❤";

组合类型

tuple

元组是临时的复合数据类型。

// a tuple of type (i32, f32)
let t = (1, 2.0); // 现在有了t.0, t.1

Vector

Vector是动态数组,大小可变。

let mut v = Vec::new();
v.push(1);
v.push(2);

let mut v1 = vec![1, 2, 3];

HashMap

use std::collections::HashMap;
let mut score = HashMap::new();
score.insert("Tom", 13);
score.insert("Joe", 16);

其他

Battery included!
std::collections下还包括:HashSet, BTreeMap, BinaryHeap, LinkedList, VecDeque, BTreeSet等。

struct

3种struct类型。

// unit struct
struct Man;
let m = Man;

// tuple struct
struct Man(String, u8);
let m = Man(String::from("Tom"), 12); // m.0, m.1;

// named-field struct
struct Man {
    name: String,
    age: u8
}
let m = Man {
    name: String::from("Tom"),
    age: 12
}; // m.name m.age;

使用impl块给struct添加实现。

impl Man {
    fn new(name: String, age: u8) -> Man {
        Man {
            name, age
        }
    }
    fn eat(&mut self, food: String) {
        // ...
    }
}
  • new是个构造函数,名字无妨,你叫create也行,不过最好遵守规范。
  • &mut self后面再讲,说明eat是个方法。

enum

enum在rust里得到了增强,每个enum值都可以附加其他数据类型。

Option

rust使用Option enum解决null引用问题。

enum Option<T> {
    None,
    Some(T),
}
struct Man {
    name: String,
    age: u8,
    job: Option<String>
}
let m = Man {
    name: String::from("Tom"),
    age: 30,
    job: Some(String::from("Farmer"))
};

Result

rust使用Result enum作为异常处理机制,所有可能异常的函数都返回Result

enum Result<T, E> {
    Ok(T),
    Err(E),
}

文件打开函数返回一个Result类型。
File::open<P: AsRef<Path>>(path: P) -> Result<File, std::io::Error>

语法

mut

没有标记mut的变量,不支持修改。

let a = 1;  // change to: let mut a = 1;
a = 2; // Error: cannot assign twice to immutable variable

表达式

rust是基于表达式的语言,很多常见的语法是表达式。(表达式有返回值)

let n = if true {
    10
} else {
    -1
};

fn add(a: i32, b: i32) -> i32 {
    a + b  // 注意没有分号,加了分号就没有返回值了。
}

解构

// destrucure tuple
let (a, b) = (1, 2);

// destrucure tuple struct
let m = Man(String::from("Tom"), 12);
let Man (name, age) = m;

// destrucure named-field struct
let m = Man {
    name: String::from("Tom"),
    age: 12
};
let Man {name, age} = m;

match

match是升级版的switch,还支持解构赋值,条件,变量绑定等。match需要穷举所有可能。可以用 _ 匹配剩下的可能。

match File::open("foo.txt") {
    Ok(f) => handle_file(f),
    Err(e) => println!("Something's wrong {}", e),
}

if let

if let语句是条件判断 + 解构。

if let Ok(f) = File::open("foo.txt") {
    handle_file(f);
}

模块

mod: 组织模块结构
use: 在当前模块引入

单文件mod

mod jim {
    pub mod money {
        pub fn pay() {
            println!("10$ to you!");
        }
    }
}
mod tom {
    pub fn charge() {
        use jim;
        jim::money::pay();
    }
}

fn main() {
    tom::charge();
}

多文件mod

rust-chart
 ├── main.rs
 ├── utils.rs
 └── chart
     ├── mod.rs
     └── kline.rs

main.rs

mod utils;
mod chart;

chart/mod.rs

pub mod kline;

utils.rs

pub fn get_size() -> (u32, u32) {
...
}

chart/kline.rs

use utils;
utils::get_size();

所有权, move & copy

任何一个变量都是属于一个作用域。作用域结束了,变量就删除了。
这个作用域就拥有这个变量的所有权。

fn main() {
    {
        let x = 1;
    }
    println!("{}", x); // Error: x is dropped
}

每个数据类型赋值要么move要么copy。
copy type: i32, u32, f32, f64 ...
move type: String, Vec, HashMap

move类型之所以是move,因为复制成本太高。
move表示所有权的转移。

fn main() {
    let n = 1;
    let n2 = n;
    println!("{} {}", n, n2);
    
    let v = vec![1, 2];
    let v2 = v;
    println!("{}", v[0]); // Error: v is moved
}

借用 & 生命周期

let a = &v1;  // a的类型 &i32
let b = &mut v2; // b的类型 &mut i32

一个变量可以拥有:

  • 多个不可变引用
  • 只能有一个可变引用, 0个不可变引用
let mut v = 1_i32;
let a = &mut v;
let b = &mut v; // Error: cannot borrow `v` as mutable more than once at a time

现在我们再试图理解这段代码:

impl Man {
    fn new(name: String, age: u8, weight: u8) -> Man {
        Man {
            name, age, weight
        }
    }
    fn eat(&mut self, food: String) {
        self.weight += 10;
        println!("{} eats a {}! Now weight: {}", self.name, food, self.weight);
    }
    fn sleep(&self) {
        println!("{} sleeps!", self.name);
    }
}

&mut self自动获取实例的可变引用。
&self自动获取实例的不可变引用。

vector, array, slice

vector大小可变,堆上分配。
array大小不可变。
slice是array或vector的一段引用。

let vector = vec![1, 2, 3, 4, 5];
let array = [2, 3];
let slice = &vector[1..3];

String, &str

&str是对String的借用(引用),是个指向String的胖指针。

let s1 = "abc"; // &'static str
let s2 = String::from("abc"); // String

生命周期

被借用变量生命周期 > 借用者生命周期。

fn main() {
    let v;
    {
        let n = 10_i32;
        v = &n; // Error: `n` does not live long enough
    }
}

编译器无法判断变量的生命周期时,需要标记生命周期。

fn main() {
    let a = 100;
    let c;
    {
        let b = 4;
        c = choose(&a, &b);
    }
    println!("{}", c);
}

fn choose<'a, 'b>(a: &'a i32, b: &'b i32) -> &'a i32 {
    a
}

trait

trait类似于其他编程语言中的接口。

  • trait使代码通用
fn calc<T: Into<f64>>(x: T) -> f64 {
    x.into() * 2.
}
calc(10_i32);
calc(10_u32);
calc(10_f32);
  • trait可以给类型增加实现
pub trait ToString {
    fn to_string(&self) -> String;
}
impl ToString for Man {
    fn to_string(&self) -> String {
        format!("Man {}", self.name)
    }
}
println!("{}", m);

甚至可以给任意类型添加实现。

trait Barker {
    fn bark(&self);
}

impl<T> Barker for T {
    fn bark(&self) {
        println!("bark bark");
    }
}

fn main() {
    31.bark();
}

Orphan rule: 只要trait或类型有一个在当前包内,才可添加trait实现。

  • trait object: dynamic dispatch
struct Dog;
struct Cat;

trait Singer {
    fn sing(&self);
}

impl Singer for Dog {
    fn sing(&self) {
        println!("woof");
    }
}

impl Singer for Cat {
    fn sing(&self) {
        println!("mew");
    }
}

fn main() {
    let a: &Singer = &Dog;
    a.sing();
    let a: &Singer = &Cat;
    a.sing();
}

Smart pointer

Box

堆上分配

let b = Box::new(1);

Rc

堆上分配+引用计数

struct Man {
    
}

RefCell & Cell

声明一个不可变对象,其内部是可变的...
RefCell borrow(), borrow_mut()方法在运行时检测借用规则,否则panic。

let c = RefCell::new(Man {
    name: "Bill".to_string(),
    age: 32
});
c.borrow_mut().age = 44;

RefCell主要用于引用类型。
Cell主要用于Copy类型。

macro

derive

#[derive(Debug)]
struct Man {
    name: String
}

println

占位 {}
占位Debug输出 {:?}
占位Debug输出+beatify {:#?}

let m = Man{
    name: String::from("Joe")
};
println!("{greet}, {:?}", m, greet="Welcome");

format

let s = format!("{greet}, {:?}", m, greet="Welcome");

工具

Cargo

创建新项目

cargo new --bin demo-rust
cargo new --lib demo-rust

Cargo.toml配置文件

[package]
name = "demo-rust"
version = "0.1.0"
authors = ["Amadeus <gliheng@gmail.com>"]

[dependencies]
futures = "0.1.17"
num = "0.1.40"
cargo run
cargo build
cargo test

std
crates.io
docs.rs

使用外部包

extern crate serde;

resources