Rust
Rust是由Mozilla主导开发的通用、编译型编程语言。设计准则为“安全、并发、实用”,支持函数式、并发式、过程式以及面向对象的编程风格。
特性:
- 强类型静态语言
编译时就知道变量类型的是静态,运行时才知道变量类型的是动态(解释性语言如python)
不允许隐式转换的是强类型,允许隐式转换的是弱类型

函数式编程
函数式编程中的函数不是指计算机中的函数,而是数学中的函数,即自变量的映射f(x)。一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都不变,所以纯函数式编程中,变量是代数中的变量,即一个值的名称,变量的值是不可变的,比如
x = x + 1在数学代数里,这个等式为假,由于变量值是不可变的,对于值的操作并不是修改原来的值,而是修改新产生的值,原来的值保持不变没有GC,不用care alloc和free,以ownership和lifetime替代
GC: garbage collection,显式申请内存,但不需要主动释放,GC在程序跑的时候会时不时的去找没有在用的memory并释放它 (Java)
alloc/free: alloc和free要配对,显式申请的内存要主动的释放 (C)
rust不需要以上两种,利用ownership和lifetime的特性,编译时就会检查很多要求,能编译过就没有内存问题(不存在空指针,内存泄露)
零开销抽象zero cost abstraction 如C++
提供面向对象,高级抽象,多态 (trait,impl,generic…. 类比C++ class,template,interface)
强大的测试系统
[test] ,单元测试的极致,在开发过程中可以测试任何一段code
安装
官网安装方法:
使用rustup,The Rust toolchain installer
获取rustup并运行:curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh
install path
rustc –version 检查安装成功
编译器为rustc,使用rustup可以配置toolchain等
编译单个文件为 rustc xxx.rs
多个文件使用cargo包管理工具
cargo用以上方法伴随着会安装
创建项目 cargo new xxx
里面会自动生成 配置文件和 主函数main.rs
Cargo.toml 是配置文件,类似于Makefile
[package]
name = “dbc”
version = “0.1.0”
authors = [“xxx”]
edition = “2018”[dependencies]
can-dbc = { path = “./can-dbc” }
clap = “2.33”
derive_more = “0.99”
socketcan = “1.7”[dependencies.nom]
version = “4.2”
features = [“verbose-errors”]
其中[dependecies] 定义了 依赖的库
执行cargo build即可编译出可执行文件或lib
vscode安装rust插件:ctrl+shitft+x
- 输入 Rust,安装
- 输入 rust-analyzer,安装
Basic
main.rs
1 | fn main() { |
followed by rust-by-example: https://doc.rust-lang.org/rust-by-example/index.html
类型
基本:
- signed integers:
i8,i16,i32,i64,i128andisize(pointer size) - unsigned integers:
u8,u16,u32,u64,u128andusize(pointer size) - floating point:
f32,f64 charUnicode scalar values like'a','α'and'∞'(4 bytes each)booleithertrueorfalse- and the unit type
(), whose only possible value is an empty tuple:()
复合:
- arrays like
[1, 2, 3] - tuples like
(1, true)
自定义:
struct: define a structure1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16struct Person {
name: String,
age: u8,
}
// A unit struct
struct Unit; //useful for generics(template)
// A tuple struct
struct Pair(i32, f32);
// A struct with two fields
struct Point {
x: f32,
y: f32,
}enum: define an enumeration1
2
3
4
5
6
7
8
9
10enum WebEvent {
// An `enum` may either be `unit-like`,
PageLoad,
PageUnload,
// like tuple structs,
KeyPress(char),
Paste(String),
// or c-like structures.
Click { x: i64, y: i64 },
}
赋值
using the let binding:
1 | let a: i32 = 1; |
默认的赋值操作都是immutable(不可修改)的,即赋值后,值不能再改了
使用mut关键字可将 不可修改的 变成 可修改的
1 | let _immutable_binding = 1; |
表达式
rust程序由表达式组成,以;结尾
1 | fn main() { |
中括号括起来的块(block)也是表达式:
1 | let y = { |
block里的最后一句就是输出的表达式,但如果加了分号;,那么输出的结果就是()
1 | let z = { |
控制
if/else
1
2
3
4
5
6
7
8
9
10let n = 5;
//if 后面接 bool condition
if n < 0 {
print!("{} is negative", n);
} else if n > 0 {
print!("{} is positive", n);
} else {
print!("{} is zero", n);
}loop/while
1
2
3
4
5
6
7
8
9
10
11loop {
//do something
continue;
//
break;
}
//loop with conditon
while n < 101 {
//do something
}for and range
使用
for in接iterator1
2
3
4
5let names = vec!["Bob", "Frank", "Ferris"];
for name in names.iter() {
println!("Hello {}", name);
}match
类似于C语言的
switch1
2
3
4
5
6
7
8
9
10
11
12let number = 13;
match number {
// Match a single value
1 => println!("One!"),
// Match several values
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
// Match an inclusive range
13..=19 => println!("A teen"),
// Handle the rest of cases
_ => println!("Ain't special"),
}match可以做destructing,很有用哦~
Destructuring Tuples
1
2
3
4
5
6
7
8
9
10
11let triple = (0, -2, 3);
// Match can be used to destructure a tuple
match triple {
// Destructure the second and third elements
(0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z),
(1, ..) => println!("First is `1` and the rest doesn't matter"),
// `..` can be the used ignore the rest of the tuple
_ => println!("It doesn't matter what they are"),
// `_` means don't bind the value to a variable
}Destructuring Enums
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
27enum Color {
// These 3 are specified solely by their name.
Red,
Blue,
Green,
// These likewise tie `u32` tuples to different names: color models.
RGB(u32, u32, u32),
HSV(u32, u32, u32),
}
fn main() {
let color = Color::RGB(122, 17, 40);
// An `enum` can be destructured using a `match`.
match color {
Color::Red => println!("The color is Red!"),
Color::Blue => println!("The color is Blue!"),
Color::Green => println!("The color is Green!"),
Color::RGB(r, g, b) =>
println!("Red: {}, green: {}, and blue: {}!", r, g, b),
Color::HSV(h, s, v) =>
println!("Hue: {}, saturation: {}, value: {}!", h, s, v),
Color::HSL(h, s, l) =>
println!("Hue: {}, saturation: {}, lightness: {}!", h, s, l),
// Don't need another arm because all variants have been examined
}
}Destructuring Structures
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19struct Foo {
x: (u32, u32),
y: u32,
}
let foo = Foo { x: (1, 2), y: 3 };
match foo {
Foo { x: (1, b), y } => println!("First of x is 1, b = {}, y = {} ", b, y),
// you can destructure structs and rename the variables,
// the order is not important
Foo { y: 2, x: i } => println!("y is 2, i = {:?}", i),
// and you can also ignore some variables:
Foo { y, .. } => println!("y = {}, we don't care about x", y),
// this will give an error: pattern does not mention field `x`
//Foo { y } => println!("y = {}", y),
}
函数
using the fn keyword,参数即跟类型声明一样,如果有返回值,则使用->
1 | // Function that returns a boolean value |
Rust注重检查返回值,在C中,通常我们使用if else等判断 返回的ret,C++中可能会实现exception来catch error,而rust的函数则比较喜欢返回 Result这个枚举
1 | enum Result<T, E> { |
其中T,E 可以是你指定的任意类型,然后通常会用match去处理返回的Result
1 | use std::fs::File; |
前面说过,match有destruted的功能,这里的返回值Result也是一个enum,其中的T和E,可以被destruted出来直接使用,很neat
类似的还有Option<T>
The Option<T> enum has two variants:
None, to indicate failure or lack of value, andSome(value), a tuple struct that wraps avaluewith typeT.
1 | // An integer division that doesn't `panic!` |
注意这个unwrap()用法,也很常用
Ownership and moves
ownership的规则如下:
- Each value in Rust has a variable that’s called its owner.
- There can only be one owner at a time.
- When the owner goes out of scope, the value will be dropped.
1 | { |
moves即为ownership的转移,通常发生在赋值或传参时,如
let x = y表示将y 给了 x,ownership转移给了x,转移后,y就不能继续使用了
对于基本类型,赋值会发生copy,其它类型才会发生move
1 | let x = 5; |
1 | let a = String::new(); //在heap上创建了内存,a指向它 |
1 | fn main() { |
Borrowing
move操作可发生在传参时,如
1 | fn func(s: String) { |
所以很多时候我们要用一个值但又不想被拿走ownership,会使用borrorw操作,传值时传reference(&T)而不是 value(T)
1 | fn func(s: &String) { |
Methods
使用impl关键字来定一个object的方法
1 | struct Point { |
另一个例子:
1 | struct Rectangle { |
rust的struct可以类比为C++的类,但很大的不同是:
C++的数据成员和成员方法都定义在类里面,而rust是分开的,struct只定义数据成员,它的方法定义在impl block body里面
可以理解为struct + impl methods实现了 一个 拥有特定成员和相应方法的object,就很像是C++的class了
Trait
A trait is a collection of methods defined for an unknown type: Self
Traits can be implemented for any data type.
类比于C++的interface
实现一个trait:
1 | pub trait Summary { |
trait可以定义default的函数,让实现它的type可以调用通用默认的函数
1 | pub trait Summary { |
和methods的区别:
trait是定义了一组方法的一种type
然后被impl给其它data type,其它data type可以使用trait里的函数或overwrite成自己的定义的同名函数
而methods是实现为某个object的方法,而不是common的interface在多个type去共用的
Trait 实现rust的多态
trait 的impl –> 有继承的味道
一般面向对象语言如C++,通过继承来实现多态
举个栗子,一个GUI库,里面有多个component如按钮,图标,多选框,都有自己的draw函数来绘制自己的组件
所以一般会实现一个 component的父类,2然后 各个子组件再继承同个父类并实现自己的draw,然后由父类引用子类来调用它自己的draw
1 | Component *com; |
多态可以做到,运行时,才确定com的引用是哪个,即运行时才会确定draw方法具体调用的哪个(运行时多态),这样可以实现出比较清晰的业务逻辑
rust的多态如下:
1 | pub trait Draw { |
static dispatch vs dynamic dispatch
static dispath就是编译时就确定具体类型的,一般是模板
1 | // define an example struct, make it printable |
dynamic dispatch就是run time时才确定具体类型的:
1 | // This function takes a pointer to a something that implements trait Bar |
不管是static还是dynamic,你都可以做到传不同类型的输入,然后得到各自类型实现的输出
这里的区别在于最后生成的code size和speed,static因为每个模板都会被编译器输出,所以会有很多类似的copies,code size比dynamic大,而dynamic为了实现它的dynamic,它多了指针和vtable,性能会低一丢丢
以上是实现了某trait的struct作为参数传入funtion,如果我们想返回一个实现了某trait的struct呢?
1 | struct Sheep {} |
这部分还有很多设计方面的思想和细节我还没体会理解到,以后再讲~
Closures
Closures are functions that can capture the enclosing environment.
1 | |val| val + x |
- using
||instead of()around input variables. - optional body delimination (
{}) for a single expression (mandatory otherwise). - the ability to capture the outer environment variables.
1 | fn main() { |
- 闭包就是在一个函数内创建立即调用的另一个函数。
- 闭包是一个匿名函数。也就是没有函数名称。
- 闭包虽然没有函数名,但可以把整个闭包赋值一个变量,通过调用该变量来完成闭包的调用。从某些方面说,这个变量就是函数名的作用。
- 闭包不用声明返回值,但它却可以有返回值。并且使用最后一条语句的执行结果作为返回值。闭包的返回值可以赋值给变量。
- 闭包有时候有些地方又称之为 内联函数。这种特性使得闭包可以访问外层函数里的变量。
1 | fn function_name(parameters) -> return_type { |
why use closure ?
- 减少重复代码
- 代码会变得简单易读( 熟练之后 :) )
- 很灵活,在迭代器里很好用
- ……
TEST
Tests are Rust functions that verify that the non-test code is functioning in the expected manner
To change a function into a test function, add #[test] on the line before fn
当执行cargo test时,rust 会编译一个可执行bin跑声明了test的函数,然后就可以看测试结果
1 |
|
当运行cargo test时,可以清楚知道运行结果,很方便做模块的单元测试
1 | $ cargo test |
并发
TODO
实际上async函数是由编辑器生成的future,await也是由编译器生成代码调用future的poll方法
Every await point is like a yield point.
Instead of yielding a value we pass in, we yield the result of calling poll on the next Future we’re awaiting.
资料
https://github.com/pretzelhammer/rust-blog/blob/master/posts/learning-rust-in-2020.md
https://fasterthanli.me/articles/a-half-hour-to-learn-rust 半小时过一遍大部分语法
https://github.com/rust-lang/rustlings 一组涵盖大部分语法和特性的半成品程序,让你去补充,使之编译test过,通过提示和编译器输出的error来做,做完会有大体的了解和熟悉
https://exercism.io/tracks/rust rust从易到难的习题,刷题的味道
https://doc.rust-lang.org/std/ 官方STD库,使用std提供的内容时,来这里查它的定义、方法、trait等
https://doc.rust-lang.org/rust-by-example/ 官方文档,绝大部分语法的example在这里面,不记得某个基础操作时来这里迅速找例子
https://doc.rust-lang.org/stable/book/title-page.html The Book, 官方圣经级别,不好读