awesome-rust
:wrench: Rust prototypes.
Crates
Contributing
Contributions are greatly appreciated. Please fork this repository and open a pull request.
Credits
Contact
Rust Basic Notes
Toolchain
Installation
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
Cargo
Cargo Basic Commands
cargo new hello_world
cargo run
cargo build
cargo run --release
cargo build --release
cargo check
cargo generate-lockfile
cargo fmt --check
cargo clippy
cargo test
cargo install cargo-edit
cargo install cargo-release
cargo install cargo-tarpaulin
cargo install cargo-watch
cargo install cargo-workspaces
Cargo release configuration:
[workspace.metadata.release]
# cargo install cargo-release
# cargo release -x
sign-commit = true
sign-tag = true
release = false
push = false
publish = false
shared-version = true
pre-release-commit-message = "chore(release): {{version}}"
post-release-commit-message = "chore(release): {{version}}"
tag-message = "{{tag_name}}"
Cargo Cache
~/.cargo/
:
config.toml
: global configuration.credentials.toml
:cargo login
related file..crates.toml
/.crates2.json
: installed package information.bin/
: installed binaries.git/
: installed rust git repositories.git/db/
: installed git repositories.git/checkouts/
: branches of git repositories.
registry/
:crates.io
metadata and packages.registry/index/
: metadata git repository.registry/cache/
: dependencies cache (.crate
gzip files).registry/src/
: package source files.
Cargo Configuration
Cargo.toml
:
cargo-features
: 只能用于nightly
版本的feature
.[package]
: 定义项目(package
)的元信息.name
: 名称.version
: 版本.authors
: 开发作者.edition
: Rust edition..rust-version
: 支持的最小化 Rust 版本.description
: 描述.documentation
: 文档 URL.readme
: README 文件的路径.homepage
: 主页 URL.repository
: 源代码仓库的 URL.license
: 开源协议 License..license-file
: License 文件的路径..keywords
: 项目的关键词.categories
: 项目分类.workspace
: 工作空间 workspace 的路径.build
: 构建脚本的路径.links
: 本地链接库的名称.exclude
: 发布时排除的文件.include
: 发布时包含的文件.publish
: 用于阻止项目的发布.metadata
: 额外的配置信息,用于提供给外部工具.default-run
: [cargo run
] 所使用的默认可执行文件( binary ).autobins
: 禁止可执行文件的自动发现.autoexamples
: 禁止示例文件的自动发现.autotests
: 禁止测试文件的自动发现.autobenches
: 禁止 bench 文件的自动发现.resolver
: 设置依赖解析器( dependency resolver).
- Cargo target configuration:
[lib]
: Library target.[[bin]]
: Binary target.[[example]]
: Example target.[[test]]
: Test target.[[bench]]
: Benchmark target.
- Dependency tables:
[dependencies]
: 项目依赖包.[dev-dependencies]
: 用于 examples、tests 和 benchmarks 的依赖包.[build-dependencies]
: 用于构建脚本的依赖包.[target]
: 平台特定的依赖包.
[badges]
: 维护状态.[features]
:features
可以用于条件编译.[patch]
: 推荐使用的依赖覆盖方式.[profile]
: 编译器设置和优化.[workspace]
: 工作空间的定义.
GitHub Action
- Use tool to speed up compilation.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
jobs:
test:
name: Test Suite
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- uses: actions-rs/cargo@v1
with:
command: test
args: --all-features --workspace
rustfmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
components: rustfmt
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
components: clippy
- name: Clippy check
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets --all-features --workspace -- -D warnings
docs:
name: Docs
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Check documentation
env:
RUSTDOCFLAGS: -D warnings
uses: actions-rs/cargo@v1
with:
command: doc
args: --no-deps --document-private-items --all-features --workspace
publish-dry-run:
name: Publish dry run
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- uses: actions-rs/cargo@v1
with:
command: publish
args: --dry-run
coverage:
name: Code coverage
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Run cargo-tarpaulin
uses: actions-rs/tarpaulin@v0.1
with:
args: --all-features --workspace --ignore-tests --out Lcov
- name: Upload to Coveralls
if: ${{ github.event_name == 'push' }}
uses: coverallsapp/github-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: ./lcov.info
Ownership
Copy Trait
Copyable type (implement Copy
trait):
- Integer type.
- Bool type.
- Float type.
- Char type.
- Copyable Tuple type, e.g
(i32, i32)
. - Reference type (borrowing ownership).
Most these types store on stack (including reference type with vtable).
fn main() { // Primitive type. let a = 5; let b = a; // Reference type. let x: &str = "hello, world"; let y = x; // Deep clone on `non-Copy` type. let s1 = String::from("hello"); let s2 = s1.clone(); // Correct. println!("a = {}, b = {}", a, b); println!("x = {}, y = {}", x, y); println!("s1 = {}, s2 = {}", s1, s2); }
fn main() { let s1 = String::from("hello"); let s2 = s1; // Error[E0382]: use of moved value: `s1`. // Move occurs because `s1` has type `std::string::String`, // which does not implement the `Copy` trait. println!("{}, world!", s1); }
Reference Type
Borrowing ownership with reference type:
- At same time, only one mutable reference or multiple immutable reference.
- Reference should be valid (rustc will report dangling reference error).
fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len); } fn calculate_length(s: &String) -> usize { s.len() // Leave function without drop `s`, // due to `s` not owner string. }
Mutable reference:
- Only one mutable reference for a value in a scope).
- Can't mutable borrow an already immutable borrowed value.
fn main() { let mut s = String::from("hello"); change(&mut s); } fn change(some_string: &mut String) { some_string.push_str(", world"); }
fn main() { let mut s = String::from("hello"); let r1 = &s; let r2 = &s; let r3 = &mut s; // Error. println!("{}, {} and {}", r1, r2, r3); // End of r1 and r2 borrowing. // Correct. let r4 = &mut s; println!("{}", r4); }
String Type
&str
string slice reference type:
- Borrowing type.
- UTF-8 encode (1 ~ 4 bytes).
- String literal is
&str
type.
#![allow(unused)] fn main() { let s = String::from("hello world"); let len = s.len(); let hello = &s[0..5]; let world = &s[6..11]; let slice1 = &s[0..2]; let slice2 = &s[..2]; let slice3 = &s[4..len]; let slice4 = &s[4..]; let slice5 = &s[0..len]; let slice6 = &s[..]; }
String
type:
- Ownership type.
- UTF-8 encode (1 ~ 4 bytes).
fn main() { let mut s = String::new(); s.push_str("hello,world"); s.push('!'); assert_eq!(s,"hello,world!"); let mut s = "hello,world".to_string(); s.push('!'); assert_eq!(s,"hello,world!"); let mut s = String::from("你好, 世界"); s.push('!'); assert_eq!(s,"你好, 世界!"); let s1 = String::from("hello,"); let s2 = String::from("world!"); let s3 = s1 + &s2; assert_eq!(s3,"hello,world!"); for c in "中国人".chars() { println!("{}", c); } }
Struct Type
#![allow(unused)] fn main() { struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn build_user(email: String, username: String) -> User { User { email, username, active: true, sign_in_count: 1, } } let user1 = User { email: String::from("someone@example.com"), username: String::from("username123"), active: true, sign_in_count: 1, }; let user2 = User { email: String::from("another@example.com"), ..user1 }; }
Tuple Struct
#![allow(unused)] fn main() { struct Color(i32, i32, i32); struct Point(i32, i32, i32); let black = Color(0, 0, 0); let origin = Point(0, 0, 0); }
newtype
: Wrap type into tuple struct:
- Make code more readable.
- Implement 3rd traits for 3rd types.
- Hide internal details of types.
#![allow(unused)] fn main() { struct Meters(u32); }
Unit-like Struct
#![allow(unused)] fn main() { struct AlwaysEqual; let subject = AlwaysEqual; impl SomeTrait for AlwaysEqual {} }
Enum Type
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } fn main() { let m1 = Message::Quit; let m2 = Message::Move{x: 1, y: 1}; let m3 = Message::ChangeColor(255, 255, 0); }
#![allow(unused)] fn main() { enum Option<T> { Some(T), None, } fn plus_one(x: Option<i32>) -> Option<i32> { match x { None => None, Some(i) => Some(i + 1), } } let five = Some(5); let six = plus_one(five); let none = plus_one(None); }
Array Type
let a: [i32; 5] = [1, 2, 3, 4, 5]; let b = [3; 5]; let slice: &[i32] = &a[1..3]; assert_eq!(slice, &[2, 3]); fn main() { let one = [1, 2, 3]; let two: [u8; 3] = [1, 2, 3]; let blank1 = [0; 3]; let blank2: [u8; 3] = [0; 3]; let arrays: [[u8; 3]; 4] = [one, two, blank1, blank2]; for a in &arrays { print!("{:?}: ", a); for n in a.iter() { print!("\t{} + 10 = {}", n, n+10); } let mut sum = 0; for i in 0..a.len() { sum += a[i]; } println!("\t({:?} = {})", a, sum); } }
Type Alias
#![allow(unused)] fn main() { type Meters = i32; let x: u32 = 5; let y: Meters = 5; println!("x + y = {}", x + y); }
#![allow(unused)] fn main() { type Result<T> = std::result::Result<T, std::io::Error>; type Thunk = Box<dyn Fn() + Send + 'static>; let f: Thunk = Box::new(|| println!("hi")); fn takes_long_type(f: Thunk) {} fn returns_long_type() -> Thunk {} }
Type Conversion
From Trait
use std::convert::From; #[derive(Debug)] struct Number { value: i32, } impl From<i32> for Number { fn from(item: i32) -> Self { Number { value: item } } } fn main() { let num = Number::from(30); println!("My number is {:?}", num); let int = 5; let num: Number = int.into(); println!("My number is {:?}", num); }
use std::convert::TryFrom; use std::convert::TryInto; #[derive(Debug, PartialEq)] struct EvenNumber(i32); impl TryFrom<i32> for EvenNumber { type Error = (); fn try_from(value: i32) -> Result<Self, Self::Error> { if value % 2 == 0 { Ok(EvenNumber(value)) } else { Err(()) } } } fn main() { // TryFrom assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8))); assert_eq!(EvenNumber::try_from(5), Err(())); // TryInto let result: Result<EvenNumber, ()> = 8i32.try_into(); assert_eq!(result, Ok(EvenNumber(8))); let result: Result<EvenNumber, ()> = 5i32.try_into(); assert_eq!(result, Err(())); }
Explicit Type Conversion
fn main() { let a = 3.1 as i8; let b = 100_i8 as i32; let c = 'a' as u8; println!("{}, {}, {}", a, b, c) let x: i16 = 1500; let x_: u8 = match x.try_into() { Ok(x1) => x1, Err(e) => { println!("{:?}", e.to_string()); 0 } }; }
Implicit Type Conversion
target.method()
:
- Call by value:
T::method(target)
. - Call by reference:
T::method(&target)
orT::method(&mut target)
. - Call by deref: when
T: Deref<Target = U>
, then(&T).method() => (&U).method()
. - Length-non-determined collection to length-determined slice.
- Panic.
#![allow(unused)] fn main() { let array: Rc<Box<[T; 3]>> = ...; let first_entry = array[0]; // 1. `Index` trait grammar sugar: array[0] => array.index(0). // 2. Call by: value: `Rc<Box<[T; 3]>>` not impl `Index` trait. // 3. Call by reference: `&Rc<Box<[T; 3]>>` not impl `Index` trait. // 4. Call by reference: `&mut Rc<Box<[T; 3]>>` not impl `Index` trait. // 5. Call by deref -> Call by value: `Box<[T; 3]>` not impl `Index` trait. // 6. Call by deref -> Call by reference: `&Box<[T; 3]>` not impl `Index` trait. // 7. Call by deref -> Call by reference: `&mut Box<[T; 3]>` not impl `Index` trait. // 8. Call by deref -> Call by deref: `[T; 3]` not impl `Index` trait. // 9. `[T; 3]` => `[T]` impl `Index` trait. }
Dynamically Sized Type
DST:
- DST 无法单独被使用, 必须要通过
&
/Box
/Rc
来间接使用. str
,[T]
,dyn Trait
.
#![allow(unused)] fn main() { // Error! let s1: str = "Hello there!"; let s2: str = "How's it going?"; // Ok. let s3: &str = "on?" let s4: Box<str> = "Hello there!".into(); }
#![allow(unused)] fn main() { // Error! fn my_function(n: usize) { let array = [123; n]; } }
#![allow(unused)] fn main() { fn foobar_1(thing: &dyn MyThing) {} // OK. fn foobar_2(thing: Box<dyn MyThing>) {} // OK. fn foobar_3(thing: Rc<dyn MyThing>) {} // OK. fn foobar_4(thing: MyThing) {} // ERROR! }
Sized Trait
Implicit sized trait:
#![allow(unused)] fn main() { fn generic<T>(t: T) {} // Auto-transform to by Rust compiler fn generic<T: Sized>(t: T) {} }
Dynamic sized generics:
#![allow(unused)] fn main() { fn generic<T: ?Sized>(t: &T) {} }
Flow Control
If Statement
if
expression:
#![allow(unused)] fn main() { let number = if condition { 5 } else { 6 }; }
if let
expression:
#![allow(unused)] fn main() { let o = Some(3); let v = if let Some(x) = o { x } else { 0 }; }
Loop Statement
For Loop Statement
#![allow(unused)] fn main() { for i in 1..=5 {} for _ in 0..10 {} for item in collection {} for item in &collection {} for item in &mut collection {} for (i, v) in collection.iter().enumerate() {} }
While Loop Statement
fn main() { let mut n = 0; while n <= 5 { println!("{}!", n); n = n + 1; } }
Loop Expression
fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; println!("The result is {}", result); }
Pattern Matching
#![allow(unused)] fn main() { match target { pattern1 => expression1, pattern2 => { statement1; statement2; expression2 }, _ => expression3 } if let pattern = target { statement; expression } while let pattern = target { statement; } }
Enum Pattern Matching
enum Action { Say(String), MoveTo(i32, i32), ChangeColorRGB(u16, u16, u16), } fn main() { let actions = [ Action::Say("Hello Rust".to_string()), Action::MoveTo(1,2), Action::ChangeColorRGB(255,255,0), ]; for action in actions { match action { Action::Say(s) => { println!("{}", s); }, Action::MoveTo(x, y) => { println!("point from (0, 0) move to ({}, {})", x, y); }, Action::ChangeColorRGB(r, g, _) => { println!("change color into '(r:{}, g:{}, b:0)', 'b' has been ignored", r, g, ); } } } }
Tuple Pattern Matching
fn main() { let numbers = (2, 4, 8, 16, 32); match numbers { (first, .., last) => { println!("Some numbers: {}, {}", first, last); }, } }
Struct Pattern Matching
struct Point { x: i32, y: i32, z: i32, } fn main() { let p = Point { x: 0, y: 7, z: 0 }; let Point { x: a, y: b, z: c } = p; assert_eq!(0, a); assert_eq!(7, b); assert_eq!(0, c); let origin = Point { x: 0, y: 0, z: 0 }; match origin { Point { x, .. } => println!("x is {}", x), } }
fn main() { let p = Point { x: 0, y: 7 }; match p { Point { x, y: 0 } => println!("On the x axis at {}", x), Point { x: 0, y } => println!("On the y axis at {}", y), Point { x, y } => println!("On neither axis: ({}, {})", x, y), } }
Match Guard
Combine pattern matching and if
expression:
#![allow(unused)] fn main() { let num = Some(4); match num { Some(x) if x < 5 => println!("less than five: {}", x), Some(x) => println!("{}", x), None => (), } }
Match Assignment
Combine pattern matching and @
expression:
#![allow(unused)] fn main() { enum Message { Hello { id: i32 }, } let msg = Message::Hello { id: 5 }; match msg { Message::Hello { id: id_variable @ 3..=7 } => { println!("Found an id in range: {}", id_variable) }, Message::Hello { id: 10..=12 } => { println!("Found an id in another range") }, Message::Hello { id } => { println!("Found some other id: {}", id) }, } }
struct Point { x: i32, y: i32, } fn main() { let p @ Point {x: px, y: py } = Point {x: 10, y: 23}; println!("x: {}, y: {}", px, py); println!("{:?}", p); let point = Point {x: 10, y: 5}; if let p @ Point {x: 10, y} = point { println!("x is 10 and y is {} in {:?}", y, p); } else { println!("x was not 10 :("); } }
Method
#![allow(unused)] fn main() { struct Circle { x: f64, y: f64, radius: f64, } impl Circle { fn new(x: f64, y: f64, radius: f64) -> Circle { Circle { x, y, radius, } } fn area(&self) -> f64 { std::f64::consts::PI * (self.radius * self.radius) } } }
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } impl Message { fn call(&self) {} } fn main() { let m = Message::Write(String::from("hello")); m.call(); }
Self
self
: 所有权转移.&self
: 不可变借用.&mut self
: 可变借用.
pub struct Rectangle { width: u32, height: u32, } impl Rectangle { pub fn new(width: u32, height: u32) -> Self { Rectangle { width, height } } pub fn width(&self) -> u32 { return self.width; } pub fn height(&self) -> u32 { return self.height; } } fn main() { let rect = Rectangle::new(30, 50); println!("{}", rect.width()); println!("{}", rect.height()); }
Generics
#![allow(unused)] fn main() { enum Option<T> { Some(T), None, } enum Result<T, E> { Ok(T), Err(E), } struct Point<T> { x: T, y: T, } impl<T> Point<T> { fn x(&self) -> &T { &self.x } fn mixup<U>(self, other: Point<U>) {} } impl Point<f32> { fn distance_from_origin(&self) -> f32 { (self.x.powi(2) + self.y.powi(2)).sqrt() } } fn add<T: std::ops::Add<T, Output = T>>(a:T, b:T) -> T { a + b } fn largest<T: PartialOrd>(list: &[T]) -> T {} }
- TurboFish:
generics_struct::<T>::method()
.struct.generics_method::<T>()
.
- Use associated types in traits.
Traits
pub struct Post { pub username: String, pub content: String } pub trait Summary { fn summarize_author(&self) -> String; fn summarize(&self) -> String { format!("(Read more from {}...)", self.summarize_author()) } } impl Summary for Post { fn summarize_author(&self) -> String { format!("@{}", self.username) } } fn main() { let post = Post{username: "username".to_string(),content: "content".to_string()}; println!("1 new post: {}", post.summarize()); }
Orphan Rule
Rust can’t implement external traits on external types:
can’t implement the Display
trait on Vec<T>
in some_package
crate,
because Display
and Vec<T>
are both defined out of some_package
.
This restriction is part of a property of programs called coherence,
ensures that other people’s code can’t break your code and vice versa.
Trait Bound
#![allow(unused)] fn main() { fn notify(item: &impl Summary) {} fn notify(item: &(impl Summary + Display)) {} fn notify<T: Summary>(item: &T) {} fn notify<T: Summary + Display>(item: &T) {} fn notify<T, U>(t: &T, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug {} }
#![allow(unused)] fn main() { trait SomeTrait: BoundTrait {} }
#![allow(unused)] fn main() { // 可以对任何实现了 Display 特征的类型调用 ToString 特征中方法. impl<T: Display> ToString for T {} }
Trait Derive
#![allow(unused)] fn main() { #[derive(Debug)] #[derive(PartialEq)] #[derive(Eq)] #[derive(PartialOrd)] #[derive(Ord)] #[derive(Clone)] #[derive(Copy)] #[derive(Hash)] #[derive(Default)] }
#![allow(unused)] fn main() { trait Person { fn name(&self) -> String; } trait Student: Person { fn university(&self) -> String; } trait Programmer { fn fav_language(&self) -> String; } trait CompSciStudent: Programmer + Student { fn git_username(&self) -> String; } fn comp_sci_student_greeting(student: &dyn CompSciStudent) -> String { format!( "My name is {} and I attend {}. My language is {}. My Git username is {}", student.name(), student.university(), student.fav_language(), student.git_username() ) } }
Trait Object
- Define trait object:
Box<dyn some_trait>
.&dyn some_trait
.
- A trait can have trait object only when
it is
object safe
:- all methods can't return
Self
. - all methods can't be generics.
- all methods can't return
- Trait object has
'static
lifetime. - Trait object stand for dynamic distributing (runtime), generics stand for static distributing (compile time).
trait Draw { fn draw(&self) -> String; } impl Draw for u8 { fn draw(&self) -> String { format!("u8: {}", *self) } } impl Draw for f64 { fn draw(&self) -> String { format!("f64: {}", *self) } } fn draw1(x: Box<dyn Draw>) { x.draw(); } fn draw2(x: &dyn Draw) { x.draw(); } fn main() { let x = 1.1f64; let y = 8u8; draw1(Box::new(x)); draw1(Box::new(y)); draw2(&x); draw2(&y); }
Associated Types
Associated types make code become readable and concise:
#![allow(unused)] fn main() { trait Container<A,B> { fn contains(&self,a: A,b: B) -> bool; } fn difference<A,B,C>(container: &C) -> i32 where C : Container<A,B> {} }
#![allow(unused)] fn main() { trait Container{ type A; type B; fn contains(&self, a: &Self::A, b: &Self::B) -> bool; } fn difference<C: Container>(container: &C) {} }
For all generic trait,
use associated types better than <T>
.
Common Traits
std::fmt::Display
(better thanstd::string::ToString
).std::fmt::Debug
.std::ops::Add
/Mul
/Div
/BitAnd
/BitOr
/Not
/Neg
: operators overload.std::ops::Fn
/FnMut
/FnOnce
.std::ops::Deref
.std::ops::Drop
.std::clone::Clone
.std::iter::Iterator
.
std::prelude
:
std::marker::{Copy, Send, Sized, Sync, Unpin}
.std::ops::{Drop, Fn, FnMut, FnOnce}
.std::mem::drop
.std::boxed::Box
.std::borrow::ToOwned
.std::clone::Clone
.std::cmp::{PartialEq, PartialOrd, Eq, Ord}
.std::convert::{AsRef, AsMut, Into, From}
.std::default::Default
.std::iter::{Iterator, Extend, IntoIterator, DoubleEndedIterator, ExactSizeIterator}
.std::option::Option::{self, Some, None}
.std::result::Result::{self, Ok, Err}
.std::string::{String, ToString}
.std::vec::Vec
.std::convert::{TryFrom, TryInto}
.std::iter::FromIterator
.
#![allow(unused)] fn main() { use std::io::prelude::*; }
Collection
Vector
Create and Insert:
#![allow(unused)] fn main() { let mut v = Vec::new(); v.push(1); }
Access and Get:
#![allow(unused)] fn main() { let v = vec![1, 2, 3, 4, 5]; let third: &i32 = &v[2]; println!("3rd: {}", third); match v.get(2) { Some(third) => println!("3rd: {}", third), None => println!("None."), } }
Visit:
#![allow(unused)] fn main() { let v = vec![1, 2, 3]; for i in &v { println!("{}", i); } }
Visit and Mutate:
#![allow(unused)] fn main() { let mut v = vec![1, 2, 3]; for i in &mut v { *i += 10 } }
Store different types:
enum IpAddr { V4(String), V6(String) } fn main() { let v = vec![ IpAddr::V4("127.0.0.1".to_string()), IpAddr::V6("::1".to_string()) ]; for ip in v { show_addr(ip) } } fn show_addr(ip: IpAddr) { println!("{:?}",ip); }
HashMap
#![allow(unused)] fn main() { use std::collections::HashMap; }
#![allow(unused)] fn main() { // Create. let mut scores = HashMap::new(); // Insert. scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); scores.entry("Red").or_insert(5); // Get. let team_name = String::from("Blue"); let score: Option<&i32> = scores.get(&team_name); // Visit for (key, value) in &scores { println!("{}: {}", key, value); } // Transform. let from_list: HashMap<_,_> = some_list.into_iter().collect(); }
HashSet
- insert.
- contains.
- union.
- difference.
- intersection.
- symmetric_difference.
Error Handling
Result Type
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!("Problem creating the file: {:?}", e), }, other_error => panic!("Problem opening the file: {:?}", other_error), }, }; }
Result Type Compositor
or
: logic or.and
: logic and.or_else
: logic or function.and_then
: logic and function.filter
:Option
filter function.map
:Ok
/Some
map function.map_or
:Ok
/Some
map function with defaults value.map_or_else
:Ok
/Some
map function with defaults function.map_err
:Err
map function.ok_or
:Option
->Result
with error message.ok_or_else
:Option
->Result
with error message function.
Error Handling Macro
?
for Result
type:
#![allow(unused)] fn main() { use std::fs::File; use std::io; use std::io::Read; fn open_file() -> Result<File, Box<dyn std::error::Error>> { let mut f = File::open("hello.txt")?; Ok(f) } fn read_username_from_file() -> Result<String, io::Error> { let mut s = String::new(); File::open("hello.txt")?.read_to_string(&mut s)?; Ok(s) } }
?
for Option
type:
#![allow(unused)] fn main() { fn last_char_of_first_line(text: &str) -> Option<char> { text.lines().next()?.chars().last() } }
Error Trait
Standard Error Trait
#![allow(unused)] fn main() { use std::fmt::{Debug, Display}; pub trait Error: Debug + Display { fn source(&self) -> Option<&(Error + 'static)> { ... } } }
use std::fs::read_to_string; use std::error::Error; fn main() -> Result<(), Box<dyn Error>> { let html = render()?; println!("{}", html); Ok(()) } fn render() -> Result<String, Box<dyn Error>> { let file = std::env::var("MARKDOWN")?; let source = read_to_string(file)?; Ok(source) }
Custom Error Type
use std::error; use std::fmt; #[derive(Debug)] struct AppError; impl fmt::Display for AppError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "An Error Occurred, Please Try Again!") } } impl error::Error for AppError { fn description(&self) -> &str { "Invalid App" } fn source(&self) -> Option<&(dyn error::Error + 'static)> { None } fn cause(&self) -> Option<&dyn error::Error> { None } } fn produce_error() -> Result<(), AppError> { Err(AppError) } fn main(){ match produce_error() { Err(e) => eprintln!("{}", e), _ => println!("No error"), } eprintln!("{:?}", produce_error()); // Err({ file: src/main.rs, line: 17 }) }
Convert From Standard Error
use std::fs::File; use std::io::{self, Read}; use std::num; #[derive(Debug)] struct AppError { kind: String, message: String, } impl From<io::Error> for AppError { fn from(error: io::Error) -> Self { AppError { kind: String::from("io"), message: error.to_string(), } } } impl From<num::ParseIntError> for AppError { fn from(error: num::ParseIntError) -> Self { AppError { kind: String::from("parse"), message: error.to_string(), } } } fn main() -> Result<(), AppError> { let mut file = File::open("hello_world.txt")?; let mut content = String::new(); file.read_to_string(&mut content)?; let _number: usize; _number = content.parse()?; Ok(()) } // --------------- 上述代码运行后的可能输出 --------------- // 01. 若 hello_world.txt 文件不存在 // Error: AppError { kind: "io", message: "No such file or directory" } // 02. 若用户没有相关的权限访问 hello_world.txt // Error: AppError { kind: "io", message: "Permission denied" } // 03. 若 hello_world.txt 包含有非数字的内容,例如 Hello, world! // Error: AppError { kind: "parse", message: "invalid digit found in string" }
Format Print
Format Print Macros
print!
.println!
.eprint!
.eprintln!
.format!
.
#![allow(unused)] fn main() { println!("Hello"); // => "Hello" println!("Hello, {}!", "world"); // => "Hello, world!" println!("The number is {}", 1); // => "The number is 1" println!("{:?}", (3, 4)); // => "(3, 4)" println!("{value}", value=4); // => "4" println!("{} {}", 1, 2); // => "1 2" println!("{:04}", 42); // => "0042" with leading zeros }
fn main() { let s = "hello"; println!("{}, world", s); let s1 = format!("{}, world", s); print!("{}", s1); print!("{}\n", "!"); }
Format Print Placeholder
{}
.{:?}
.{:#?}
.- Index placeholder.
- Alias placeholder.
fn main() { println!("{1}{}{0}{}", 1, 2); // => 2112 println!("{name} {}", 1, name = 2); // => "2 1" println!("{a} {c} {b}", a = "a", b = 'b', c = 3); // => "a 3 b" }
- Pad placeholder.
fn main() { // 以下全部输出 "Hello x !" // 为"x"后面填充空格, 补齐宽度5 println!("Hello {:5}!", "x"); // 使用参数5来指定宽度 println!("Hello {:1$}!", "x", 5); // 使用x作为占位符输出内容, 同时使用5作为宽度 println!("Hello {1:0$}!", 5, "x"); // 使用有名称的参数作为宽度 println!("Hello {:width$}!", "x", width = 5); // 使用参数5为参数x指定宽度, 同时在结尾输出参数5 => Hello x !5 println!("Hello {:1$}!{}", "x", 5); // 宽度是5 => Hello 5! println!("Hello {:5}!", 5); // 显式的输出正号 => Hello +5! println!("Hello {:+}!", 5); // 宽度5, 使用0进行填充 => Hello 00005! println!("Hello {:05}!", 5); // 负号也要占用一位宽度 => Hello -0005! println!("Hello {:05}!", -5); }
- Alignment placeholder.
fn main() { // 以下全部都会补齐5个字符的长度 // 左对齐 => Hello x ! println!("Hello {:<5}!", "x"); // 右对齐 => Hello x println!("Hello {:>5}!", "x"); // 居中对齐 => Hello x ! println!("Hello {:^5}!", "x"); // 对齐并使用指定符号填充 => Hello x&&&&! // 指定符号填充的前提条件是必须有对齐字符 println!("Hello {:&<5}!", "x"); }
- Precision placeholder.
fn main() { let v = 3.1415926; // 保留小数点后两位 => 3.14 println!("{:.2}", v); // 带符号保留小数点后两位 => +3.14 println!("{:+.2}", v); // 不带小数 => 3 println!("{:.0}", v); // 通过参数来设定精度 => 3.1416, 相当于{:.4} println!("{:.1$}", v, 4); let s = "hello I'm some one"; // 保留字符串前三个字符 => hel println!("{:.3}", s); // {:.*} 接收两个参数, 第一个是精度, 第二个是被格式化的值 => Hello abc! println!("Hello {:.*}!", 3, "abcdefg"); }
- Radix placeholder:
boxXeE
.fmt::Binary
trait.fmt::Octal
trait.fmt::LowerHex
trait.fmt::UpperHex
trait.fmt::LowerExp
trait.fmt::UpperExp
trait.
fn main() { // 二进制 => 0b11011! println!("{:#b}!", 27); // 八进制 => 0o33! println!("{:#o}!", 27); // 十进制 => 27! println!("{}!", 27); // 小写十六进制 => 0x1b! println!("{:#x}!", 27); // 大写十六进制 => 0x1B! println!("{:#X}!", 27); // 不带前缀的十六进制 => 1b! println!("{:x}!", 27); // 使用0填充二进制, 宽度为10 => 0b00011011! println!("{:#010b}!", 27); println!("{:2e}", 1000000000); // => 1e9 println!("{:2E}", 1000000000); // => 1E9 }
Debug Trait
#[derive(Debug)] struct Person { name: String, age: u8 } fn main() { let i = 3.1415926; let s = String::from("hello"); let v = vec![1, 2, 3]; let p = Person{name: "name".to_string(), age: 18}; println!("{:?}, {:?}, {:?}, {:?}", i, s, v, p); }
Display Trait
use std::fmt; struct Person { name: String, age: u8, } impl fmt::Display for Person { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "My name is {}, {} year old.", self.name, self.age ) } } fn main() { let p = Person { name: "name".to_string(), age: 18, }; println!("{}", p); }
Lifetime
显式地使用生命周期, 可以让编译器正确地认识到多个引用之间的关系.
#![allow(unused)] fn main() { &i32 // 一个引用 &'a i32 // 具有显式生命周期的引用 &'a mut i32 // 具有显式生命周期的可变引用 }
#![allow(unused)] fn main() { struct ImportantExcerpt<'a> { part: &'a str, } fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } }
Function Lifetime
函数或者方法中,
参数的生命周期被称为输入生命周期
,
返回值的生命周期被称为输出生命周期
:
- 每一个引用参数都会获得独自的生命周期:
fn foo<'a, 'b>(x: &'a i32, y: &'b i32)
. - 若只有一个输入生命周期, 则该生命周期会被赋给所有输出生命周期.
- 若存在多个输入生命周期, 且其中一个是
&self
/&mut self
, 则&self
生命周期被赋给所有输出生命周期 (除非显式地声明输出生命周期).
Static Lifetime
生命周期'static
表示持续整个程序,
例如字符串字面量和特征对象.
Lifetime Constraint
'a: 'b
:'a
生命周期更长.T: 'a
:T
生命周期更长.
Closure
Function Parameter Closure
改变捕获变量的所有权 (FnOnce):
fn fn_once<F>(func: F) where F: FnOnce(usize) -> bool + Copy, { println!("{}", func(3)); println!("{}", func(4)); } fn main() { let x = vec![1, 2, 3]; fn_once(|z|{z == x.len()}) }
可变借用捕获 (FnMut):
fn main() { let mut s = String::new(); let update_string = |str| s.push_str(str); exec(update_string); println!("{:?}",s); } fn exec<'a, F: FnMut(&'a str)>(mut f: F) { f("hello") }
不可变借用捕获 (Fn):
fn main() { let s = "hello, ".to_string(); let update_string = |str| println!("{},{}",s,str); exec(update_string); println!("{:?}",s); } fn exec<'a, F: Fn(String) -> ()>(f: F) { f("world".to_string()) }
- 所有闭包都自动实现了
FnOnce
特征, 因此任何一个闭包都至少可以被调用一次. - 没有移出所捕获变量的所有权的闭包自动实现了
FnMut
特征. - 不需要对捕获变量进行改变的闭包自动实现了
Fn
特征.
Function Return Closure
#![allow(unused)] fn main() { fn factory() -> impl Fn(i32) -> i32 { let num = 5; |x| x + num } }
#![allow(unused)] fn main() { fn factory(x:i32) -> Box<dyn Fn(i32) -> i32> { let num = 5; if x > 1{ Box::new(move |x| x + num) } else { Box::new(move |x| x - num) } } }
Iterator
#![allow(unused)] fn main() { let arr = [1, 2, 3]; for v in arr.into_iter() { println!("{}", v); } }
fn main() { let arr = [1, 2, 3]; let mut arr_iter = arr.into_iter(); assert_eq!(arr_iter.next(), Some(1)); assert_eq!(arr_iter.next(), Some(2)); assert_eq!(arr_iter.next(), Some(3)); assert_eq!(arr_iter.next(), None); }
Iterator Trait
#![allow(unused)] fn main() { pub trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; } impl<I: Iterator> IntoIterator for I { type Item = I::Item; type IntoIter = I; #[inline] fn into_iter(self) -> I { self } } }
iter
: 不可变借用.iter_mut
: 可变借用.into_iter
: 改变所有权.
#![allow(unused)] fn main() { fn iter(&self) -> Iter // Iter implements Iterator<Item = &U> fn iter_mut(&mut self) -> IterMut // IterMut implements Iterator<Item = &mut U> fn into_iter(self) -> IntoIter // IntoIter implements Iterator<Item = U> }
fn main() { let values = vec![1, 2, 3]; for v in values.into_iter() { println!("{}", v) } // 下面的代码将报错. // println!("{:?}",values); let values = vec![1, 2, 3]; let _values_iter = values.iter(); // 不会报错. println!("{:?}", values); let mut values = vec![1, 2, 3]; // 对 values 中的元素进行可变借用. let mut values_iter_mut = values.iter_mut(); // 取出第一个元素, 并修改为0. if let Some(v) = values_iter_mut.next() { *v = 0; } // 输出 [0, 2, 3]. println!("{:?}", values); }
Implement iterator:
struct Counter { count: u32, } impl Counter { fn new() -> Counter { Counter { count: 0 } } } impl Iterator for Counter { type Item = u32; fn next(&mut self) -> Option<Self::Item> { if self.count < 5 { self.count += 1; Some(self.count) } else { None } } } fn main() { let mut counter = Counter::new(); assert_eq!(counter.next(), Some(1)); assert_eq!(counter.next(), Some(2)); assert_eq!(counter.next(), Some(3)); assert_eq!(counter.next(), Some(4)); assert_eq!(counter.next(), Some(5)); assert_eq!(counter.next(), None); let sum: u32 = Counter::new() .zip(Counter::new().skip(1)) .map(|(a, b)| a * b) .filter(|x| x % 3 == 0) .sum(); assert_eq!(18, sum); }
Adapter Methods
- 消费性适配器: 获取迭代器的所有权, 并消耗迭代器中所有元素.
collect::<T>()
.- fold.
- partition.
sum::<T>()
.
- 迭代性适配器: 惰性方法 (Lazy Iterator)
- enumerate.
- filter.
- filter_map.
- map.
- take_while.
- zip.
- Ordinary iterator methods:
- Iterator::any.
- Iterator::find.
More adapter methods see Iterator
trait
documentation.
fn main() { let v1 = vec![1, 2, 3]; let v1_iter = v1.iter(); let total: i32 = v1_iter.sum(); assert_eq!(total, 6); // v1_iter 是借用了 v1, 因此 v1 可以照常使用. println!("{:?}",v1); // 以下代码会报错, 因为 `sum` 拿到了迭代器 `v1_iter` 的所有权. // println!("{:?}",v1_iter); }
fn main() { let v1: Vec<i32> = vec![1, 2, 3]; let v2: Vec<_> = v1.iter().map(|x| x + 1).collect(); assert_eq!(v2, vec![2, 3, 4]); }
use std::collections::HashMap; fn main() { let names = ["name1", "name2"]; let ages = [18, 18]; let folks: HashMap<_, _> = names.into_iter().zip(ages.into_iter()).collect(); println!("{:?}",folks); }
Smart Pointer
Box Type
Box<T>
将一个值分配到堆上, 然后在栈上保留一个智能指针指向堆上数据:
- 实现转移所有权时的零拷贝.
- 将不定长类型转换为定长类型.
fn main() { // 在栈上创建一个长度为 1000 的数组. let arr = [0;1000]; // 将 arr 所有权转移 arr1, 由于 `arr` 分配在栈上, 因此直接重新深拷贝了一份数据. let arr1 = arr; // arr 和 arr1 都拥有各自的栈上数组, 因此不会报错. println!("{:?}", arr.len()); println!("{:?}", arr1.len()); // 在堆上创建一个长度为 1000 的数组, 然后使用一个智能指针指向它. let arr = Box::new([0;1000]); // 将堆上数组的所有权转移给 arr1, 由于数据在堆上, 因此仅仅拷贝了智能指针的结构体, 底层数据并没有被拷贝. // 所有权顺利转移给 arr1, arr 不再拥有所有权. let arr1 = arr; println!("{:?}", arr1.len()); // 由于 arr 不再拥有底层数组的所有权, 因此下面代码将报错. // println!("{:?}", arr.len()); }
#![allow(unused)] fn main() { enum List { Cons(i32, Box<List>), Nil, } }
Deref Trait
&smart_pointer
=>smart_pointer.defer()
.*smart_pointer
=>*(smart_pointer.defer())
.smart_pointer.method()
=>(&smart_pointer).method()
=>(smart_pointer.defer()).method()
.- When
T: Deref<Target=U>
, then&T => &U
. - When
T: DerefMut<Target=U>
, then&mut T => &mut U
. - When
T: Deref<Target=U>
, then&mut T => &U
.
use core::ops::{self}; use crate::str::{self, from_boxed_utf8_unchecked}; use crate::vec::Vec; struct String { vec: Vec<u8>, } impl ops::Deref for String { type Target = str; fn deref(&self) -> &str { unsafe { str::from_utf8_unchecked(&self.vec) } } } struct MyBox<T>(T); impl<T> MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) } } impl<T> ops::Deref for MyBox<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } fn main() { let x = MyBox::new(5); assert_eq!(5, *x); // => *(x.deref()) // => *(&x.0) // => x.0 let s = MyBox::new(String::from("hello world")); display(&s); // => &MyBox // => MyBox.deref() // => &String // => String.deref() // => &str let hello_world = MyBox::new(String::from("hello, world")); let s1: &str = &hello_world; // => &MyBox<String> // => MyBox<String>.deref() // => &String // => String.deref() // => &str let s2: String = hello_world.to_string(); // => MyBox<String>.to_string() // => (&MyBox<String>).to_string() // => (MyBox<String>.defer()).to_string() // => (&String).to_string() let ptr: *const u8 = hello_world.as_ptr(); // => MyBox<String>.as_ptr() // => (&MyBox<String>).as_ptr() // => (MyBox<String>.defer()).as_ptr() // => (&String).as_ptr() // => (String.defer()).as_ptr() // => (&str).as_ptr() } fn display(s: &str) { println!("{}", s); }
Drop Trait
Drop order:
- 变量级别, 按照逆序的方式, 先创建的变量后 drop.
- 结构体内部, 按照顺序的方式, 结构体中的字段按照定义中的顺序依次 drop.
Reference Counting Type
通过引用计数的方式, 允许一个数据资源在同一时刻拥有多个所有者.
use std::rc::Rc; fn main() { let a = Rc::new(String::from("hello, world")); let b = Rc::clone(&a); // 复制了智能指针并增加了引用计数, 并没有克隆底层数据. assert_eq!(2, Rc::strong_count(&a)); assert_eq!(Rc::strong_count(&a), Rc::strong_count(&b)) }
Rc
/Arc
是不可变引用, 无法修改它指向的值.Rc<T>
是一个智能指针, 实现了Deref
特征, 可以直接使用T
.- 一旦最后一个拥有者消失, 则资源会自动被回收.
Arc
: Atomic reference counting.
use std::sync::Arc; use std::thread; fn main() { let s = Arc::new(String::from("Multiple threads walker")); for _ in 0..10 { let s = Arc::clone(&s); let handle = thread::spawn(move || { println!("{}", s) }); } }
Cell and RefCell Type
Cell
for copyable type.
use std::cell::Cell; fn main() { let c = Cell::new("abc"); let one = c.get(); c.set("xyz"); let two = c.get(); println!("{}, {}", one, two); // abc, xyz }
#![allow(unused)] fn main() { use std::cell::Cell; fn retain_even(nums: &mut Vec<i32>) { let slice: &[Cell<i32>] = Cell::from_mut(&mut nums[..]) .as_slice_of_cells(); let mut i = 0; for num in slice.iter().filter(|num| is_even(num.get())) { slice[i].set(num.get()); i += 1; } nums.truncate(i); } }
RefCell
for borrowing reference:
- 实现内部可变性: 不可变值的可变借用.
imut_self.refcell_member.borrow_mut().changeMember()
. Rc<RefCell<T>>
: 实现多个可变数据所有者.- 实现编译期可变借用与不可变借用共存,
但会引起运行时
panic
.
use std::cell::RefCell; fn main() { let s = RefCell::new(String::from("hello, world")); let s1 = s.borrow(); let s2 = s.borrow_mut(); println!("{}, {}", s1, s2); }
通过包裹一层 RefCell
,
将不可变借用 &self
的成员成为一个可变值,
然后实现修改:
use std::cell::RefCell; pub trait Messenger { fn send(&self, msg: String); } pub struct MsgQueue { msg_cache: RefCell<Vec<String>>, } impl Messenger for MsgQueue { fn send(&self, msg: String) { self.msg_cache.borrow_mut().push(msg) } } fn main() { let mq = MsgQueue { msg_cache: RefCell::new(Vec::new()), }; mq.send("hello, world".to_string()); }
Circle Reference
Weak
通过 use std::rc::Weak
引入, 具有以下特点:
- 可访问, 但没有所有权, 不增加引用计数, 不影响 drop.
- 可由
Rc<T>
调用downgrade
方法转换成Weak<T>
. Weak<T>
可使用upgrade
方法转换成Option<Rc<T>>
, 如果资源已经被释放, 则Option
的值是None
.- 常用于解决循环引用的问题.
use std::cell::RefCell; use std::rc::{Rc, Weak}; #[derive(Debug)] struct Node { value: i32, parent: RefCell<Weak<Node>>, children: RefCell<Vec<Rc<Node>>>, } fn main() { let leaf = Rc::new(Node { value: 3, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![]), }); println!( "leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf), ); { let branch = Rc::new(Node { value: 5, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![Rc::clone(&leaf)]), }); *leaf.parent.borrow_mut() = Rc::downgrade(&branch); println!( "branch strong = {}, weak = {}", Rc::strong_count(&branch), Rc::weak_count(&branch), ); println!( "leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf), ); } println!("leaf parent = {:?}", leaf.parent.borrow().upgrade()); println!( "leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf), ); }
Phantom Type
虚类型/幽灵类型参数是一种在运行时不出现, 仅进行静态编译检查的类型参数.
#![allow(unused)] fn main() { use std::marker::PhantomData; struct Iter<'a, T: 'a> { ptr: *const T, end: *const T, _marker: PhantomData<&'a T>, } struct Vec<T> { data: *const T, len: usize, cap: usize, _marker: PhantomData<T>, } }
use std::marker::PhantomData; #[derive(PartialEq)] struct PhantomTuple<A, B>(A, PhantomData<B>); #[derive(PartialEq)] struct PhantomStruct<A, B> { first: A, phantom: PhantomData<B> } fn main() { let _tuple1: PhantomTuple<char, f32> = PhantomTuple('Q', PhantomData); let _tuple2: PhantomTuple<char, f64> = PhantomTuple('Q', PhantomData); let _struct1: PhantomStruct<char, f32> = PhantomStruct { first: 'Q', phantom: PhantomData, }; let _struct2: PhantomStruct<char, f64> = PhantomStruct { first: 'Q', phantom: PhantomData, }; // 编译期错误!类型不匹配,所以这些值不能够比较: println!("_tuple1 == _tuple2 yields: {}", _tuple1 == _tuple2); // 编译期错误!类型不匹配,所以这些值不能够比较: println!("_struct1 == _struct2 yields: {}", _struct1 == _struct2); }
use std::ops::Add; use std::marker::PhantomData; #[derive(Debug, Clone, Copy)] enum Inch {} #[derive(Debug, Clone, Copy)] enum Mm {} #[derive(Debug, Clone, Copy)] struct Length<Unit>(f64, PhantomData<Unit>); impl<Unit> Add for Length<Unit> { type Output = Length<Unit>; fn add(self, rhs: Length<Unit>) -> Length<Unit> { Length(self.0 + rhs.0, PhantomData) } } fn main() { let one_foot: Length<Inch> = Length(12.0, PhantomData); let one_meter: Length<Mm> = Length(1000.0, PhantomData); let two_feet = one_foot + one_foot; let two_meters = one_meter + one_meter; println!("one foot + one_foot = {:?} in", two_feet.0); println!("one meter + one_meter = {:?} mm", two_meters.0); // 编译期错误: 类型不匹配. let compile_error = one_foot + one_meter; }
Concurrent Programming
Concurrency Programming Model
Name | Pros | Cons |
---|---|---|
OS Thread | simple, native model | consistent and context switch overhead |
Event Driven | perf model | non-liner logic, callback hell |
Coroutines | perf model | non-system abstraction |
Actor | distributed model | complex flow control and retry logic |
Async/Await | perf, native model | complex internal logic |
OS Threads
for CPU intensive task (parallel computing),
Async/Await
for I/O intensive task (blocking I/O).
Threads
use std::thread; use std::time::Duration; fn main() { let handle = thread::spawn(|| { for i in 1..5 { println!("hi number {} from the spawned thread!", i); thread::sleep(Duration::from_millis(1)); } }); for i in 1..5 { println!("hi number {} from the main thread!", i); thread::sleep(Duration::from_millis(1)); } handle.join().unwrap(); }
Barrier
use std::sync::{Arc, Barrier}; use std::thread; fn main() { let mut handles = Vec::with_capacity(6); let barrier = Arc::new(Barrier::new(6)); for _ in 0..6 { let b = barrier.clone(); handles.push(thread::spawn(move|| { println!("before wait"); b.wait(); println!("after wait"); })); } for handle in handles { handle.join().unwrap(); } }
Condition Variables and Mutex
use std::thread; use std::sync::{Arc, Mutex, Condvar}; fn main() { let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair2 = pair.clone(); thread::spawn(move|| { let &(ref lock, ref cvar) = &*pair2; let mut started = lock.lock().unwrap(); println!("changing started"); *started = true; cvar.notify_one(); }); let &(ref lock, ref cvar) = &*pair; let mut started = lock.lock().unwrap(); while !*started { started = cvar.wait(started).unwrap(); } println!("started changed"); }
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *counter.lock().unwrap()); }
Read and write mutex:
- 同时允许多个读, 但最多只能有一个写.
- 读和写不能同时存在.
- 可以使用
read
/try_read
/write
/try_write
.
use std::sync::RwLock; fn main() { let lock = RwLock::new(5); // 同一时间允许多个读. { let r1 = lock.read().unwrap(); let r2 = lock.read().unwrap(); assert_eq!(*r1, 5); assert_eq!(*r2, 5); } // Drop. // 同一时间只允许一个写. { let mut w = lock.write().unwrap(); *w += 1; assert_eq!(*w, 6); } // Drop. }
Threads Communication
Message channel:
use std::sync::mpsc; use std::thread; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { tx.send(1).unwrap(); }); // Block. println!("receive {}", rx.recv().unwrap()); }
Sync channel with message buffer:
use std::sync::mpsc; use std::thread; use std::time::Duration; fn main() { // Sync channel with 3 length buffer. let (tx, rx)= mpsc::sync_channel(3); let handle = thread::spawn(move || { println!("发送之前"); tx.send(1).unwrap(); println!("发送之后"); }); println!("睡眠之前"); thread::sleep(Duration::from_secs(3)); println!("睡眠之后"); println!("receive {}", rx.recv().unwrap()); handle.join().unwrap(); }
Send message via for
loop:
use std::sync::mpsc; use std::thread; use std::time::Duration; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let values = vec![ String::from("hi"), String::from("from"), String::from("the"), String::from("thread"), ]; for value in values { tx.send(value).unwrap(); thread::sleep(Duration::from_secs(1)); } }); for received in rx { println!("Got: {}", received); } }
Multiple producers:
use std::sync::mpsc; use std::thread; fn main() { let (tx, rx) = mpsc::channel(); let tx1 = tx.clone(); thread::spawn(move || { tx.send(String::from("hi from raw tx")).unwrap(); }); thread::spawn(move || { tx1.send(String::from("hi from cloned tx")).unwrap(); }); for received in rx { println!("Got: {}", received); } }
Multiple type message:
use std::sync::mpsc::{self, Receiver, Sender}; enum Fruit { Apple(u8), Orange(String) } fn main() { let (tx, rx): (Sender<Fruit>, Receiver<Fruit>) = mpsc::channel(); tx.send(Fruit::Orange("sweet".to_string())).unwrap(); tx.send(Fruit::Apple(2)).unwrap(); for _ in 0..2 { match rx.recv().unwrap() { Fruit::Apple(count) => println!("received {} apples", count), Fruit::Orange(flavor) => println!("received {} oranges", flavor), } } }
Tokio Semaphore
use std::sync::Arc; use tokio::sync::Semaphore; #[tokio::main] async fn main() { let semaphore = Arc::new(Semaphore::new(3)); let mut join_handles = Vec::new(); for _ in 0..5 { let permit = semaphore.clone().acquire_owned().await.unwrap(); join_handles.push(tokio::spawn(async move { /** * Task here ... */ drop(permit); })); } for handle in join_handles { handle.await.unwrap(); } }
Atomic Primitives
use std::ops::Sub; use std::sync::atomic::{AtomicU64, Ordering}; use std::thread::{self, JoinHandle}; use std::time::Instant; const N_TIMES: u64 = 10000000; const N_THREADS: usize = 10; static R: AtomicU64 = AtomicU64::new(0); fn add_n_times(n: u64) -> JoinHandle<()> { thread::spawn(move || { for _ in 0..n { R.fetch_add(1, Ordering::Relaxed); } }) } fn main() { let s = Instant::now(); let mut threads = Vec::with_capacity(N_THREADS); for _ in 0..N_THREADS { threads.push(add_n_times(N_TIMES)); } for thread in threads { thread.join().unwrap(); } assert_eq!(N_TIMES * N_THREADS as u64, R.load(Ordering::Relaxed)); println!("{:?}",Instant::now().sub(s)); }
Ordering
内存顺序:
- Relaxed: 乱序.
- Release: 设置内存屏障, 保证它之前的操作永远在它之前.
- Acquire: 设置内存屏障, 保证它之后的操作永远在它之后.
- AcqRel: Acquire + Release.
- SeqCst: 顺序一致性.
use std::thread::{self, JoinHandle}; use std::sync::atomic::{Ordering, AtomicBool}; static mut DATA: u64 = 0; static READY: AtomicBool = AtomicBool::new(false); fn reset() { unsafe { DATA = 0; } READY.store(false, Ordering::Relaxed); } fn producer() -> JoinHandle<()> { thread::spawn(move || { unsafe { DATA = 100; // A } READY.store(true, Ordering::Release); // B: 内存屏障 ↑ }) } fn consumer() -> JoinHandle<()> { thread::spawn(move || { while !READY.load(Ordering::Acquire) {} // C: 内存屏障 ↓ assert_eq!(100, unsafe { DATA }); // D }) } fn main() { loop { reset(); let t_producer = producer(); let t_consumer = consumer(); t_producer.join().unwrap(); t_consumer.join().unwrap(); } }
Spinlock:
use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{hint, thread}; fn main() { let spinlock = Arc::new(AtomicUsize::new(1)); let spinlock_clone = Arc::clone(&spinlock); let thread = thread::spawn(move|| { spinlock_clone.store(0, Ordering::SeqCst); }); // 等待其它线程释放锁. while spinlock.load(Ordering::SeqCst) != 0 { hint::spin_loop(); } if let Err(panic) = thread.join() { println!("Thread had an error: {:?}", panic); } }
Send and Sync Trait
Send and Sync:
- Marker trait.
- 实现
Send
的类型可以在线程间安全的传递其所有权, 实现Sync
的类型可以在线程间安全的共享 (通过引用). 若&T: Send
, 则T: Sync
. - 绝大部分类型都实现了
Send
/Sync
, 例外: 原生指针,Cell
/RefCell
,Rc
.
Thread Pool
#![allow(unused)] fn main() { use std::sync::mpsc; use std::sync::Arc; use std::sync::Mutex; use std::thread; type Job = Box<dyn FnOnce() + Send + 'static>; enum Message { NewJob(Job), Terminate, } pub struct ThreadPool { workers: Vec<Worker>, sender: mpsc::Sender<Message>, } impl ThreadPool { /// Create a new ThreadPool. /// /// The size is the number of threads in the pool. /// /// # Panics /// /// The `new` function will panic if the size is zero. pub fn new(size: usize) -> ThreadPool { assert!(size > 0); let (sender, receiver) = mpsc::channel(); let receiver = Arc::new(Mutex::new(receiver)); let mut workers = Vec::with_capacity(size); for id in 0..size { workers.push(Worker::new(id, Arc::clone(&receiver))); } ThreadPool { workers, sender } } pub fn execute<F>(&self, f: F) where F: FnOnce() + Send + 'static, { let job = Box::new(f); self.sender.send(Message::NewJob(job)).unwrap(); } } impl Drop for ThreadPool { fn drop(&mut self) { println!("Sending terminate message to all workers."); for _ in &self.workers { self.sender.send(Message::Terminate).unwrap(); } println!("Shutting down all workers."); for worker in &mut self.workers { println!("Shutting down worker {}", worker.id); if let Some(thread) = worker.thread.take() { thread.join().unwrap(); } } } } struct Worker { id: usize, thread: Option<thread::JoinHandle<()>>, } impl Worker { fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker { let thread = thread::spawn(move || loop { let message = receiver.lock().unwrap().recv().unwrap(); match message { Message::NewJob(job) => { println!("Worker {} got a job; executing.", id); job(); } Message::Terminate => { println!("Worker {} was told to terminate.", id); break; } } }); Worker { id, thread: Some(thread), } } } }
Asynchronous Programming
Async and Await
- 在
.await
执行期间, 任务可能会在线程间转移. .await
只能用于 async fn 函数中.
use futures::executor::block_on; use futures::join; async fn learn_song() -> Song { /* ... */ } async fn sing_song(song: Song) { /* ... */ } async fn dance() { /* ... */ } async fn learn_and_sing() { let song = learn_song().await; sing_song(song).await; } async fn async_main() { let f1 = learn_and_sing(); let f2 = dance(); join!(f1, f2); } fn main() { block_on(async_main()); }
use futures::future; use futures::select; pub fn main() { let mut a_fut = future::ready(4); let mut b_fut = future::ready(6); let mut total = 0; loop { select! { a = a_fut => total += a, b = b_fut => total += b, complete => break, default => panic!(), // 该分支永远不会运行. }; } assert_eq!(total, 10); }
Future Trait
Future
代表一组计算, 惰性求值. 当.await
调用时才真正开始执行.Future
启动后会因资源等原因阻塞, 转入pending
状态.Future
阻塞后, 当资源准备好可以重新启动时, 会通过Waker.wake
通知执行器, 等待被下一次poll
.
#![allow(unused)] fn main() { trait Future { type Output; fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll<Self::Output>; } }
#![allow(unused)] fn main() { use std::{ future::Future, pin::Pin, sync::{Arc, Mutex}, task::{Context, Poll, Waker}, thread, time::Duration, }; pub struct TimerFuture { shared_state: Arc<Mutex<SharedState>>, } struct SharedState { completed: bool, waker: Option<Waker>, } impl Future for TimerFuture { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { let mut shared_state = self.shared_state.lock().unwrap(); if shared_state.completed { Poll::Ready(()) } else { shared_state.waker = Some(cx.waker().clone()); Poll::Pending } } } impl TimerFuture { pub fn new(duration: Duration) -> Self { let shared_state = Arc::new(Mutex::new(SharedState { completed: false, waker: None, })); let thread_shared_state = shared_state.clone(); thread::spawn(move || { thread::sleep(duration); let mut shared_state = thread_shared_state.lock().unwrap(); shared_state.completed = true; if let Some(waker) = shared_state.waker.take() { waker.wake() } }); TimerFuture { shared_state } } } }
当 Future
会返回 Poll::Pending
时,
一定要确保 wake
能被正常调用,
否则会导致任务永远被挂起,
再也不会被执行器 poll
.
use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::{Duration, Instant}; struct Delay { when: Instant, } impl Future for Delay { type Output = &'static str; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<&'static str> { if Instant::now() >= self.when { println!("Hello world"); Poll::Ready("done") } else { let waker = cx.waker().clone(); let when = self.when; thread::spawn(move || { let now = Instant::now(); if now < when { thread::sleep(when - now); } waker.wake(); }); Poll::Pending } } } #[tokio::main] async fn main() { let when = Instant::now() + Duration::from_millis(10); let future = Delay { when }; let out = future.await; assert_eq!(out, "done"); }
Asynchronous Runtime
use { futures::{ future::{BoxFuture, FutureExt}, task::{waker_ref, ArcWake}, }, std::{ future::Future, sync::mpsc::{sync_channel, Receiver, SyncSender}, sync::{Arc, Mutex}, task::{Context, Poll}, time::Duration, }, // 引入之前实现的定时器模块 timer_future::TimerFuture, }; struct Task { future: Mutex<Option<BoxFuture<'static, ()>>>, task_sender: SyncSender<Arc<Task>>, } impl ArcWake for Task { fn wake_by_ref(arc_self: &Arc<Self>) { let cloned = arc_self.clone(); arc_self .task_sender .send(cloned) .expect("任务队列已满"); } } #[derive(Clone)] struct Spawner { task_sender: SyncSender<Arc<Task>>, } impl Spawner { fn spawn(&self, future: impl Future<Output = ()> + 'static + Send) { let future = future.boxed(); let task = Arc::new(Task { future: Mutex::new(Some(future)), task_sender: self.task_sender.clone(), }); self.task_sender.send(task).expect("任务队列已满"); } } struct Executor { ready_queue: Receiver<Arc<Task>>, } impl Executor { fn run(&self) { while let Ok(task) = self.ready_queue.recv() { let mut future_slot = task.future.lock().unwrap(); if let Some(mut future) = future_slot.take() { let waker = waker_ref(&task); let context = &mut Context::from_waker(&*waker); if future.as_mut().poll(context).is_pending() { // Future 未执行完,, 将它放回任务中, 等待下次被 poll. *future_slot = Some(future); } } } } } fn new_executor_and_spawner() -> (Executor, Spawner) { const MAX_QUEUED_TASKS: usize = 10_000; let (task_sender, ready_queue) = sync_channel(MAX_QUEUED_TASKS); (Executor { ready_queue }, Spawner { task_sender }) } fn main() { let (executor, spawner) = new_executor_and_spawner(); spawner.spawn(async { println!("howdy!"); TimerFuture::new(Duration::new(2, 0)).await; println!("done!"); }); drop(spawner); // 运行执行器直到任务队列为空. // 任务运行后, 会先打印 `howdy!`, 暂停 2 秒, 接着打印 `done!`. executor.run(); }
use std::cell::RefCell; use std::future::Future; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll, Waker}; use std::thread; use std::time::{Duration, Instant}; use futures::task::{self, ArcWake}; use crossbeam::channel; fn main() { let mini_tokio = MiniTokio::new(); mini_tokio.spawn(async { spawn(async { delay(Duration::from_millis(100)).await; println!("world"); }); spawn(async { println!("hello"); }); delay(Duration::from_millis(200)).await; std::process::exit(0); }); mini_tokio.run(); } struct MiniTokio { scheduled: channel::Receiver<Arc<Task>>, sender: channel::Sender<Arc<Task>>, } impl MiniTokio { fn new() -> MiniTokio { let (sender, scheduled) = channel::unbounded(); MiniTokio { scheduled, sender } } fn spawn<F>(&self, future: F) where F: Future<Output = ()> + Send + 'static, { Task::spawn(future, &self.sender); } fn run(&self) { CURRENT.with(|cell| { *cell.borrow_mut() = Some(self.sender.clone()); }); while let Ok(task) = self.scheduled.recv() { task.poll(); } } } pub fn spawn<F>(future: F) where F: Future<Output = ()> + Send + 'static, { CURRENT.with(|cell| { let borrow = cell.borrow(); let sender = borrow.as_ref().unwrap(); Task::spawn(future, sender); }); } async fn delay(dur: Duration) { struct Delay { when: Instant, waker: Option<Arc<Mutex<Waker>>>, } impl Future for Delay { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { if let Some(waker) = &self.waker { let mut waker = waker.lock().unwrap(); if !waker.will_wake(cx.waker()) { *waker = cx.waker().clone(); } } else { let when = self.when; let waker = Arc::new(Mutex::new(cx.waker().clone())); self.waker = Some(waker.clone()); thread::spawn(move || { let now = Instant::now(); if now < when { thread::sleep(when - now); } let waker = waker.lock().unwrap(); waker.wake_by_ref(); }); } if Instant::now() >= self.when { Poll::Ready(()) } else { Poll::Pending } } } let future = Delay { when: Instant::now() + dur, waker: None, }; future.await; } thread_local! { static CURRENT: RefCell<Option<channel::Sender<Arc<Task>>>> = RefCell::new(None); } struct Task { future: Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>, executor: channel::Sender<Arc<Task>>, } impl Task { fn spawn<F>(future: F, sender: &channel::Sender<Arc<Task>>) where F: Future<Output = ()> + Send + 'static, { let task = Arc::new(Task { future: Mutex::new(Box::pin(future)), executor: sender.clone(), }); let _ = sender.send(task); } fn poll(self: Arc<Self>) { let waker = task::waker(self.clone()); let mut cx = Context::from_waker(&waker); let mut future = self.future.try_lock().unwrap(); let _ = future.as_mut().poll(&mut cx); } } impl ArcWake for Task { fn wake_by_ref(arc_self: &Arc<Self>) { let _ = arc_self.executor.send(arc_self.clone()); } }
#[tokio::main] async fn main() { println!("Hello world"); } fn main() { tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .unwrap() .block_on(async { println!("Hello world"); }) }
Tests
#![allow(unused)] fn main() { fn greeting(name: &str) -> String { format!("Hello {}!", name) } #[cfg(test)] mod tests { use super::*; #[test] #[ignore] #[should_panic] #[should_panic(expected = "Panic message.")] fn greeting_contains_name() { let target = "name"; let result = greeting("Name"); assert!( result.contains(target), "Expect: `{}`, Result: `{}`", target, result ); } } }
IO
Path
use std::path::Path; fn main() { let path = Path::new("."); let new_path = path.join("a").join("b"); // 将路径转换成一个字符串切片 match new_path.to_str() { None => panic!("new path is not a valid UTF-8 sequence"), Some(s) => println!("new path is {}", s), } // `display` 方法返回一个可显示的结构体 let display = path.display(); }
Files
File::open
.File::create
.file.read_to_string
.file.write_all
.bufReader.lines
.OpenOptions
.fs::read_dir
.fs::create_dir
.fs::create_dir_all
.fs::remove_file
.fs::remove_dir
.fs::symlink
.
use std::error::Error; use std::fs::File; use std::io::prelude::*; use std::path::Path; fn main() { let path = Path::new("hello.txt"); let display = path.display(); let mut file = match File::open(&path) { Err(why) => panic!("couldn't open {}: {}", display, why.description()), Ok(file) => file, }; let mut s = String::new(); match file.read_to_string(&mut s) { Err(why) => panic!("couldn't read {}: {}", display, why.description()), Ok(_) => print!("{} contains:\n{}", display, s), } }
use std::fs::File; use std::io::{self, BufRead}; use std::path::Path; fn main() { if let Ok(lines) = read_lines("./hosts") { for line in lines { if let Ok(ip) = line { println!("{}", ip); } } } } fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> where P: AsRef<Path>, { let file = File::open(filename)?; Ok(io::BufReader::new(file).lines()) }
static LOREM_IPSUM: &'static str = "Words"; use std::error::Error; use std::fs::File; use std::io::prelude::*; use std::path::Path; fn main() { let path = Path::new("out/lorem_ipsum.txt"); let display = path.display(); let mut file = match File::create(&path) { Err(why) => panic!("couldn't create {}: {}", display, why.description()), Ok(file) => file, }; match file.write_all(LOREM_IPSUM.as_bytes()) { Err(why) => { panic!("couldn't write to {}: {}", display, why.description()) }, Ok(_) => println!("successfully wrote to {}", display), } }
use std::fs; use std::fs::{File, OpenOptions}; use std::io; use std::io::prelude::*; use std::os::unix; use std::path::Path; fn cat(path: &Path) -> io::Result<String> { let mut f = File::open(path)?; let mut s = String::new(); match f.read_to_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), } } fn echo(s: &str, path: &Path) -> io::Result<()> { let mut f = File::create(path)?; f.write_all(s.as_bytes()) } fn touch(path: &Path) -> io::Result<()> { match OpenOptions::new().create(true).write(true).open(path) { Ok(_) => Ok(()), Err(e) => Err(e), } } fn main() { println!("`mkdir a`"); match fs::create_dir("a") { Err(why) => println!("! {:?}", why.kind()), Ok(_) => {}, } println!("`echo hello > a/b.txt`"); echo("hello", &Path::new("a/b.txt")).unwrap_or_else(|why| { println!("! {:?}", why.kind()); }); println!("`mkdir -p a/c/d`"); fs::create_dir_all("a/c/d").unwrap_or_else(|why| { println!("! {:?}", why.kind()); }); println!("`touch a/c/e.txt`"); touch(&Path::new("a/c/e.txt")).unwrap_or_else(|why| { println!("! {:?}", why.kind()); }); println!("`ln -s ../b.txt a/c/b.txt`"); if cfg!(target_family = "unix") { unix::fs::symlink("../b.txt", "a/c/b.txt").unwrap_or_else(|why| { println!("! {:?}", why.kind()); }); } println!("`cat a/c/b.txt`"); match cat(&Path::new("a/c/b.txt")) { Err(why) => println!("! {:?}", why.kind()), Ok(s) => println!("> {}", s), } println!("`ls a`"); match fs::read_dir("a") { Err(why) => println!("! {:?}", why.kind()), Ok(paths) => for path in paths { println!("> {:?}", path.unwrap().path()); }, } println!("`rm a/c/e.txt`"); fs::remove_file("a/c/e.txt").unwrap_or_else(|why| { println!("! {:?}", why.kind()); }); println!("`rmdir a/c/d`"); fs::remove_dir("a/c/d").unwrap_or_else(|why| { println!("! {:?}", why.kind()); }); }
System
Process
use std::process::Command; fn main() { let output = Command::new("rustc") .arg("--version") .output().unwrap_or_else(|e| { panic!("failed to execute process: {}", e) }); if output.status.success() { let s = String::from_utf8_lossy(&output.stdout); print!("rustc succeeded and stdout was:\n{}", s); } else { let s = String::from_utf8_lossy(&output.stderr); print!("rustc failed and stderr was:\n{}", s); } }
use std::error::Error; use std::io::prelude::*; use std::process::{Command, Stdio}; static PROGRAM: &'static str = "the quick brown fox jumped over the lazy dog\n"; fn main() { let process = match Command::new("wc") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() { Err(why) => panic!("couldn't spawn wc: {}", why.description()), Ok(process) => process, }; match process.stdin.unwrap().write_all(PROGRAM.as_bytes()) { Err(why) => panic!("couldn't write to wc stdin: {}", why.description()), Ok(_) => println!("sent program to wc"), } let mut s = String::new(); match process.stdout.unwrap().read_to_string(&mut s) { Err(why) => panic!("couldn't read wc stdout: {}", why.description()), Ok(_) => print!("wc responded with:\n{}", s), } }
Command Line
use std::env; use std::error::Error; use std::fs; use std::process; pub struct Config { pub query: String, pub filename: String, pub case_sensitive: bool, } impl Config { pub fn new(args: &[String]) -> Result<Config, &'static str> { if args.len() < 3 { return Err("not enough arguments"); } let query = args[1].clone(); let filename = args[2].clone(); let case_sensitive = env::var("CASE_INSENSITIVE").is_err(); Ok(Config { query, filename, case_sensitive, }) } } pub fn run(config: Config) -> Result<(), Box<dyn Error>> { let contents = fs::read_to_string(config.filename)?; let results = if config.case_sensitive { search(&config.query, &contents) } else { search_case_insensitive(&config.query, &contents) }; for line in results { println!("{}", line); } Ok(()) } pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { let mut results = Vec::new(); for line in contents.lines() { if line.contains(query) { results.push(line); } } results } pub fn search_case_insensitive<'a>( query: &str, contents: &'a str, ) -> Vec<&'a str> { let query = query.to_lowercase(); let mut results = Vec::new(); for line in contents.lines() { if line.to_lowercase().contains(&query) { results.push(line); } } results } fn main() { let args: Vec<String> = env::args().collect(); let config = Config::new(&args).unwrap_or_else(|err| { eprintln!("Problem parsing arguments: {}", err); process::exit(1); }); if let Err(e) = run(config) { eprintln!("Application error: {}", e); process::exit(1); } }
Macros
#![allow(unused)] fn main() { #[macro_export] macro_rules! vec { ( $( $x:expr ),* ) => { { let mut temp_vec = Vec::new(); $( temp_vec.push($x); )* temp_vec } }; } }
Unsafe Code
unsafe {}
:
- 对原始指针进行解引用.
- 调用
不安全
的函数 (包括 C 函数, 编译器内建指令, 原始分配器). - 实现
不安全
的特性. - 访问
union
字段. - 改变静态数据.
Comments
#![allow(unused)] fn main() { // Line Comments /* Block Comments */ /// Document Line Comments /** Document Block Comments */ //! Crate Line Comments /*! Crate Block Comments */ /// [`Option`] /// [`Type`](struct@Type) /// [`Type`](fn@Type) #[doc(alias = "alias" )] }
Attributes
- Crate scope:
#![crate_attribute]
. - Module/Function scope:
#[item_attribute]
.
#![allow(unused)] fn main() { #[attribute = "value"] #[attribute(key = "value")] #[attribute(value)] }
Crate Attributes
#![allow(unused)] #![crate_type = "lib"] #![crate_name = "rand"] fn main() { }
Linter Attributes
#![allow(unused)] fn main() { #[allow(dead_code)] #[allow(unused)] }
Compile Attributes
#[cfg(target_os = "linux")] fn are_you_on_linux() { println!("You are running linux!") } #[cfg(not(target_os = "linux"))] fn are_you_on_linux() { println!("You are *not* running linux!") } fn main() { are_you_on_linux(); if cfg!(target_os = "linux") { println!("Yes. It's definitely linux!"); } else { println!("Yes. It's definitely *not* linux!"); } }
Standard Library
as_
:borrowed
->borrowed
.into_
:owned
->owned
(移除所有权).to_
:borrowed
->borrowed
,borrowed
->owned
on non-copy types,owned
->owned
on copy types.try_
: 尝试一次, 失败则返回或报错._mut
: 可变借用.
Web Development
Node.js Bindings
Tasks suite for native Node.js
add-ons:
- Computing intensive tasks with simple I/O:
e.g
@node-rs/crc32
(CPU SIMD instruction),@node-rs/bcrypt
,@node-rs/jieba
. - System call tasks: SIMD instruction, GPU instruction.
Napi:
Framework for building compiled Node.js
add-ons in Rust
via Node API.
#![allow(unused)] fn main() { #[macro_use] extern crate napi; /// import the preludes use napi::bindgen_prelude::*; /// module registration is done by the runtime, no need to explicitly do it now. #[napi] fn fibonacci(n: u32) -> u32 { match n { 1 | 2 => 1, _ => fibonacci(n - 1) + fibonacci(n - 2), } } /// use `Fn`, `FnMut` or `FnOnce` traits to defined JavaScript callbacks /// the return type of callbacks can only be `Result`. #[napi] fn get_cwd<T: Fn(String) -> Result<()>>(callback: T) { callback(env::current_dir().unwrap().to_string_lossy().to_string()).unwrap(); } /// or, define the callback signature in where clause #[napi] fn test_callback<T>(callback: T) where T: Fn(String) -> Result<()> {} /// async fn, require `async` feature enabled. /// [dependencies] /// napi = {version="2", features=["async"]} #[napi] async fn read_file_async(path: String) -> Result<Buffer> { tokio::fs::read(path) .map(|r| match r { Ok(content) => Ok(content.into()), Err(e) => Err(Error::new( Status::GenericFailure, format!("failed to read file, {}", e), )), }) .await } }
Neon:
Rust
bindings for safe and fast native Node.js
modules.
use neon::context::{Context, ModuleContext, FunctionContext}; use neon::types::JsNumber; use neon::result::JsResult; use neon::result::NeonResult; fn fibonacci(n: i32) -> i32 { return match n { n if n < 1 => 0, n if n <= 2 => 1, _ => fibonacci(n - 1) + fibonacci(n - 2) } } fn fibonacci_api(mut cx: FunctionContext) -> JsResult<JsNumber> { let handle = cx.argument::<JsNumber>(0).unwrap(); let res = fibonacci(handle.value(&mut cx) as i32); Ok(cx.number(res)) } #[neon::main] fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("fibonacci_rs", fibonacci_api)?; Ok(()) }
const { fibonacci_rs } = require('./index.node');
const value = process.argv[2] || null;
const number = parseInt(value);
if (isNaN(number)) {
console.log('Provided value is not a number');
return;
}
const result = fibonacci_rs(number);
console.log(result);
Library
- Num: Numeric Types and Traits
- Rand: Random Number Generator
- Regex: Regular Expression Engine
- Chrono: DateTime Library
- AsyncStd: Asynchronous Version Standard Library
- Crossbeam: Concurrent Programming Tools
- Tokio: Asynchronous Runtime
- Rayon: Data Parallelism Library
- Log: Logging Library
- Tracing: Tracing Library
- Serde: Serialization Framework
- Axum: Tokio Web Framework
- Rocket: Web Framework
- Actix: Web Framework
- Warp: Web Framework
- Request: HTTP Client
- Quiche: QUIC and HTTP/3 Library
- Tonic: gPRC Framework
- QuickWit: Distributed Search Engine
- MeiliSearch: Realtime Search Engine
- Clap: CLI Framework
- Console: ProgressBar
- Syn: Source Code Parser
- Napi: Node.js Bindings Library:
- Neon: Node.js Bindings Library:
- Git: Git Bindings Library
- PrettyAssertions
- Criterion: Benchmarking Library
- Clog: Conventional Changelog