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
使用外部包
extern crate serde;