1. Getting Started
- 使用
rustup
安装,默认安装在$HOME/.cargo
和$HOME/.rustup
下 rustup update
rustup self uninstall
- cargo 常用命令
cargo new PROJECT
cargo build [--release]
cargo run
2. Programming a Guessing Game
Cargo.toml
里依赖部分的版本号 “MAJOR.MINOR.PATCH” 实际是 “^MAJOR.MINOR.PATCH” 的简写,表示允许语义版本的升级cargo update
默认只升级 PATCH 部分cargo doc --open
查看文档
3. Common Programming Concepts
- 不可变变量:
let VAR: TYPE = VALUE;
- 可变变量:
let mut VAR: TYPE = VALUE;
- 常量:
const FOO_BAR: TYPE = VALUE;
- Shadowing: 同一作用域里,同名变量可以重复声明,之前声明的变量被遮蔽。
- Scalar types:
- Integer types: i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize,后两者长度跟机器相关,一般用于集合、数组的索引和长度。Debug 模式下溢出回绕会panic,Release模式下溢出回绕不报错。使用标准库里类型 Wrapping 显示表明期望溢出回绕行为。
- Integer literals: 98_222, 0xff, 0o77, 0b1111_0000, b’A’,除了 byte literal,其它字面量都支持类型后缀,比如 57u8 表示一个 u8 类型的值 57。整型字面量默认类型为 i32。
- Floating-point types: f32, f64,默认为 f64。
- Boolean type: bool, true, false。布尔类型长度为一字节。
- Character type: char,四个字节,表示一个 Unicode Scalar Value, 范围为 [U+0000, U+D7FF] 和 [U+E000, U+10FFFF]。
- Compound Types
- Tuple: (x, y, z),类型声明 (t1, t2, t3)
- Array: [x, y, z],类型声明 [t; size]。 使用语法 [x; n] 创建 n 个 x 值的数组。数据访问会检查是否越界。
- Functions:
fn foo(x:t1, y:t2) -> t3 { ... }
- Control flow:
if condition { ... } else if condition { ... } else { ... }
loop { .... break VALUE; ... }
while condition { ... }
for VAR in ITER { ... }
4. Understanding Ownership
-
Ownership rules:没有实现 Copy trait 的类型,变量赋值时是 move 语义,owner 超出作用域时自动调用 drop()
- 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.
-
Reference: borrow 语义
- 多个只读引用可以同时指向一个变量
- 同时只能有一个可写引用指向一个变量,不能有其它只读或者可写引用指向同一个变量
- Non-Lexical Lifetimes(NLL): 引用的作用域从声明开始,直到它最后一次被使用位置,并不是按照词法作用域判断
-
Slice: borrow 语义
&s[i..j]
表示[i, j)
范围&str
表示 string slice,定义字符串函数时,经常使用&str
作为参数类型,以同时支持 String 和 &str- slice 记录第一个元素的引用,以及一个长度字段
5. Using Structs to Structure Related Data
-
定义结构体:
struct Xxx { f1: t1, f2: t2 }
-
新建实例:
Xxx { f1: v1, f2: v2 }
-
当变量名与字段名同名时可以简写
Xxx { f1, f2: v2}
等价于Xxx { f1: f1, f2: v2}
。 -
根据已有结构体实例修改部分字段以创建新实例:
Xxx { f1: v1, ..xxx}
,xxx 为已有实例 -
Tuple Struct:
struct Point(i32, i32, i32)
可以使用point.0
,point.1
来引用字段。 -
Unit-like struct:
()
,没有任何字段。 -
在结构体定义时使用 annotation
#[derive(Debug)]
可以让它能被println!
的{:?}
或者{:#?}
置位符输出。 -
定义方法:
impl Rectangle { fn area(&self) -> u32 { self.width * self.height } }
-
同一个结构体可以有多个
impl
块。
6. Enums and Pattern Matching
-
定义:
enum Message { Quit, Move { x: i32, y: i32 }, // 匿名 struct Write(String), ChangeColor(i32, i32, i32), }
-
enum 跟 struct 一样,也可以用
impl
定义 method -
Option
类型包含在 prelude 中,可以直接使用Option
甚至它的 variantsSome
和None
。enum Option<T> { Some(T), None, }
-
match 表达式:
match expr { pattern => ..., _ => ..., }
-
if let 表达式:
if let pattern = expr { ...; } else { ...; }
7. Managing Growing Projects with Packages, Crates, and Modules
-
Rust module system:
- Packages: A Cargo feature that lets you build, test, and share crates
- Crates: A tree of modules that produces a library or executable
- Modules and use: Let you control the organization, scope, and privacy of paths
- Paths: A way of naming an item, such as a struct, function, or module
-
一个 package 的目录结构如下,src/lib.rs 和 src/main.rs 的 crate name 与 package 同名
package/ 一个 package 最多有一个 library crate,可以有多个 binary crate ├── Cargo.toml └── src ├── bin │ ├── bar.rs # 额外的 binary crate,隐含 module "crate",此文件称为 crate root │ └── foo.rs # 额外的 binary crate,隐含 module "crate",此文件称为 crate root ├── lib.rs # 默认的 library crate,隐含 module "crate",此文件称为 crate root └── main.rs # 默认的 binary crate,隐含 module "crate",此文件称为 crate root
-
module tree 和 directory tree 不是一一对应的,一个
.rs
文件里可以定义平行或者嵌套的多个 module。module tree 里的 path 规则:- 绝对路径:当前 crate
crate::...
,其它 cratesome_crate::...
- 相对路径:
self::...
,super::...
,some_identifier
- 绝对路径:当前 crate
-
符号的可见性
- 所有符号,包括 mod, struct, enum, fn, const 默认都是私有的,需要用 pub 修饰符公开
- child mod 可以看到 parent mod 的所有符号,parent mod 只能看到 child mod 公开的符号
- pub struct 的 field 需要额外加 pub 才是公开的,而 pub enum 的 variants 都是公开的
-
使用
use
导入 path,类似于文件系统中ln -s PATH .
- 习惯上,导入 function 时,导入到 mod 级别,使用
some_mod::some_fn
调用,以容易识别函数来自哪个模块 - 习惯上,导入 struct 和 enum 时,导入到符号本身级别,比如
use std::collectioons::HashMap;
- 使用
as
关键字导入为别名:use std::io::Result as IoResult;
use
导入的符号默认为私有,使用pub use
达到 re-exporting 为公开的效果。- Nested paths:
use std::{cmp::Ordering, io};
等价于use std::cmp::Ordering; use std::io;
,use std::io::{self, Write};
等价于use std::io; use std::io::Write;
- 引入一个 mod 里的所有公开符号:
use std::collections::*;
,一般用于单元测试代码里。
- 习惯上,导入 function 时,导入到 mod 级别,使用
-
切分 modules 到不同文件
mod xxx;
等价于mod xxx { ...加载 module/path/to/xxx.rs 文件内容.... }
- 在 crate root 里,
mod xxx
; 等价于mod xxx { ...load xxx.rs... }
- 在 crate::foo::bar 里,
mod xxx;
等价于mod xxx { ... load foo/bar/xxx.rs... }
- 在 crate root 里,
use
只是做符号连接,并不会加载模块
8. Common Collections
-
Vec<T>
: 创建实例Vec::new()
,vec![1, 2, 3, 4]
-
v[i]
在 i 越界时会 panic,v.get(i)
返回Option<T>
,因此在越界时返回 None。 -
字符串的
+
调用的fn add(self, s: &str) -> String
,第一个参数的所有权会被拿走,后面的参数必须是引用。 -
format!(...)
拼接字符串,不拿走任何参数的所有权。 -
字符串的
len()
返回的是「字节数」不是「字符数」! 使用s.chars().count()
可以拿到 unicode scalar value 个数,而通常人为感知的「字符」指 grapheme cluster,需要用第三方库处理。 -
HashMap:
use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); // 如果已经存在,则会覆盖 let team_name = String::from("Blue"); let score = scores.get(&team_name); // 返回 Option<&V> for (key, value) in &scores { ... } // 不存在时才插入,返回 &mut V let score = scores.entry(String::from("Blue")).or_insert(50); *score += 10;
-
HashMap 默认使用 cryptographically strong SipHash,如果感觉性能更重要,可以换用其它 hasher。
9. Error Handling
-
不可恢复错误:
panic!
,默认使用 unwind 策略,释放栈上的对象,在 Cargo.toml 里使用 profile 换成 abort 策略,也即立即退出。[profile.release] panic = 'abort'
-
使用
RUST_BACKTRACE=1
环境变量在 panic 时打印调用栈 -
可恢复错误:
enum Result<T, E> { Ok(T), Err(E), }
-
?
操作符:someResult?
表示如果是 Ok 则返回 T,如果是 Err,则转换成当前函数返回类型(必须是Result)并返回。
10. Generic Types, Traits, and Lifetimes
-
struct, enum, fn 都可以使用泛型,在类型或者函数名字后面使用
<T1, T2, ...>
添加泛型参数。 -
impl<T> Point<T> { ... }
中 impl 后的泛型参数,表示Point<T>
里的T
是泛型参数,而不是具体参数。 -
Trait 很像其它语言的 interface,支持默认实现。
// 定义 trait trait Summary { fn summarize(&self) -> String; fn abstract(&self) -> String { String::from("....") } } // 为某个 struct or enum 实现 trait impl Summary for Tweet { fn summarize(&self) -> String { format!("...", ...) } } // 要求函数参数满足某个 trait,这种写法一般用在参数个数少的情况 fn notify(item: impl Summary) { .... } // 更通用的 Trait 约束语法 fn notify<T: Summary>(item: T) { ... } // 满足多个 trait fn notify(item: impl Summary + Display) { ... } fn notify<T: Summary + Display>(item: T) { ... } fn notify<T>(item: T) where T: Summary + Display { ... } // 更容易阅读函数签名 // 函数返回值满足某个 trait,注意由于 Rust 编译器实现限制, // impl Summary 返回值约束下,函数只能固定返回某种固定的具体类型! fn return_summarizable() -> impl Summary { ... }
-
利用 trait bound 来条件化的实现方法
use std::fmt::Display; struct Pair<T> { x: T, y: T, } impl<T> Pair<T> { fn new(x: T, y: T) -> Self { Self { x, y, } } } impl<T: Display + PartialOrd> Pair<T> { fn cmp_display(&self) { if self.x >= self.y { println!("The largest member is x = {}", self.x); } else { println!("The largest member is y = {}", self.y); } } }
-
blanket implentation: 为满足某个 trait bound 的所有类型实现 method。
impl<T: Display> ToString for T { fn to_string(&self) -> String { ... } }
-
Lifetime 也是一种泛型参数,修饰于引用类型上,比如
&'a TYPE
,lifetime 名字紧接&
后面,以单引号开头,任意字符串作为名字,习惯上跟泛型类型参数一样用单个字符。作为泛型参数的一种,其也需要在 struct, enum, fn 的名字后面用<'a, 'b>
这样指定上, 对于impl<'a, 'b>'
也如此。fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { ... } struct ImportantExcerpt<'a> { part: &'a str, } impl<'a> ImportantExcerpt<'a> { ... }
-
函数签名 lifetime 省略的三条规则(Rust 编译器以后可能加入更多规则以方便代码编写)。当 lifetime annotation 没有针对函数的输入参数(对应input lifetime parameter)和返回值(对应output lifetime parameter) 指定时:
- 每个没有指定 lifetime 的输入参数获得自己独有的 lifetime
- 如果只有一个输入参数,且返回值没有指定 lifetime,则返回值和输入参数有同样的 lifetime
- 如果第一个参数是
&self
or&mut self
(意味着这个函数是个method),且返回值没有指定 lifetime,则返回值和 self 有同样的 lifetime
-
'static
称为静态生命周期,指代一个引用在整个程序运行期间都有效,比如字面字符串的引用,其隐含了'static
生命周期。注意编译器在提示 “lifetime'static
required” 时往往是错的,程序员应该正确指定合适的 lifetime。
11. Writing Automated Tests
-
单元测试代码示例:
#[cfg(test)] mod tests { use super::*; #[test] #[should_panic(expected = "...some error message...")] fn it_works() { assert_eq!(2 + 2, 4); } }
-
assert!
,assert_eq!
,assert_ne!
都可以接受额外的错误消息模板以及参数,类似format!
-
测试函数可以返回 Result<(), String>,成功时返回
Ok(())
,失败时返回Err(String::from("..."))
-
对于 library crate,
tests/
目录下每个.rs
文件被当作一个 crate,cargo test
会执行每个文件里的测试函数。通用的 setup 代码可以放到tests/common/mod.rs
里,不要放到tests/common.rs
因为这样会被当作一个集成测试文件。use adder; mod common; #[test] fn it_adds_two() { common::setup(); assert_eq!(4, adder::add_two(2)); }
12. An I/O Project: Building a Command Line Program
-
std::env::args()
需要命令行参数是合法的 Unicode,否则会 panic。对非 Unicode 字符集使用std::env::args_os()
-
std::fs
操作文件 -
std::process::exit(i32)
退出进程 -
可能遇到任意错误的返回值:
use std::error::Error; fn main() -> Result<(), Box<dyn Error>> { ... }
-
std::env::var(KEY)
获取环境变量 -
eprintln!(...)
输出到 stderr
13. Functional Language Features: Iterators and Closures
-
Closure 语法:
fn add_one_v1 (x: u32) -> u32 { x + 1 } let add_one_v2 = |x: u32| -> u32 { x + 1 }; let add_one_v3 = |x| { x + 1 }; let add_one_v4 = |x| x + 1 ;
-
Closure 的参数、返回值类型在第一次使用 closure 时才推断确定。
-
Closure 在实现时,实际是创建了一个匿名 struct,实现了 trait
FnOnce
,FnMut
,Fn
中的一个,并保存了捕获的上下文变量。 -
在 closure 参数列表前加
move
表示显式的将捕获的环境变量的所有权转移到 closure 上,这种用法一般用在把 closure 传递给另一个线程时。let x = vec![1, 2, 3]; let equal_to_x = move |z| z == x;
-
Iterator trait:
pub trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; // methods with default implementations elided }
-
Iterator 的三个构造方法:
- iter(): return immutable reference
- iter_mut(): return mutable reference
- into_iter(): take ownership and return owned value
14. More About Cargo and Crates.io
-
crate 级别(src/lib.rs) 开头以及 module 开头,使用
//! ...
编写 Markdown 文档 -
struct, enum, fn 之类前面使用
/// ...
编写 Markdown 文档,一般包含:# Examples
: 使用 ``` 包围的代码段,在执行cargo test
时会被当成测试用例执行# Panics
: 是否调用了 panic!# Errors
: 对 Result 返回值什么情况下返回 Err 的阐述# Safefy
: 如何使用 unsafe
-
Cargo workspace: 在顶层目录创建 Cargo.toml,然后在此目录下再
cargo new
创建 package,所有 package 共享顶层目录的 Cargo.lock,但各自的 Cargo.toml 完全独立。[workspace] members = [ "pkg1", "pkg2" ]
-
Cargo workspace 里 package 之间的 library crate 依赖:
# pkg1/Cargo.toml [dependencies] pkg2 = { path = "../pkg2" }
-
使用
cargo run -p PKG
和cargo test -p PKG
局限于单个 package。 -
使用
cargo install PKG
安装 binary crate -
PATH 里带有
cargo-
前缀的命令都自动成为cargo
的子命令,可以用这个方式扩展 Cargo。
15. Smart Pointers
-
smart pointers 指实现了 trait
Deref
和Drop
的 struct,从这个意义上讲,String
和Vec
也是。Box<T>
: 在堆上分配内存Rc<T>
: 引用技术,支持多个 ownerRef<T>
,RefMut<T>
使用RefCell<T>
访问,运行时应用 borrowing 规则
-
实现一个智能指针
use std::ops::Deref; struct MyBox<T>(T); // 定义 tuple struct impl<T> Deref for MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) } type Target = T; // 需要类型 fn deref(&self) -> &T { &self.0 // 返回 tuple 第一个元素的引用 } }
-
创建 Box
: let x = Box::new(String::from("xxx"));
-
隐式的 Deref Coercion:
- From
&T
to&U
whenT: Deref
- From
&mut T
to&mut U
whenT: DerefMut
- From
&mut T
to&U
whenT: Deref
- From
-
使用
drop
函数提前显式地析构对象。 -
引用计数
use std::rc::Rc; let x = Rc::new(String::from("xxx")); let y = Rc::clone(&x); println!("y's strong_count={}", Rc::strong_count(&y));
-
Rc
只能用在单线程里,并且只能共享不可变引用。std::cell::RefCell 也只能用在单线程。 -
interior mutability
指不可变的类型内部通过 unsafe 代码可以安全的修改数据,RefCell
是一个典型的例子。 -
The reasons to choose
Box
,Rc
, orRefCell
:Rc
enables multiple owners of the same data;Box
andRefCell
have single owners.Box
allows immutable or mutable borrows checked at compile time;Rc
allows only immutable borrows checked at compile time;RefCell
allows immutable or mutable borrows checked at runtime.- Because
RefCell
allows mutable borrows checked at runtime, you can mutate the value inside theRefCell
even when theRefCell
is immutable.
-
RefCell<T>::borrow()
返回Ref<T>
,RefCell<T>::borrow_mut()
返回RefMut<T>
,两个函数可以当作&
和&mut
看待。 -
Rc<T>
用于多个 owner 对同一个数据的只读访问,Rc<RefCell<T>>
用于多个 owner 对同一个数据的可写访问。 -
Rc::downgrade(&Rc<T>)
得到Weak<T>
引用,用Rc::upgrade(&Weak<T>)
得到Option<Rc<T>>
。
16. Fearless Concurrency
-
新建线程:
use std::thread; fn main() { let handle = thread::spawn(move || { ... }); handle.join().unwrap(); }
-
使用 multiple producer, single consumer channel 跨线程通讯
use std::sync::mpsc; use std::thread; use std::time::Duration; fn main() { let (tx, rx) = mpsc::channel(); // tx 指 transmiter, rx 指 reciever let tx2 = mpsc::Sender::clone(&tx); // multiple producer thread::spawn(move || { tx.send(String::from("hello")).unwrap(); thread::sleep(Duration::from_secs(1)); }); thread::spawn(move || { tx2.send(String::from("world")).unwrap(); thread::sleep(Duration::from_secs(1)); }); let received = rx.recv().unwrap(); // 阻塞式 recv(), 非阻塞式 try_recv() println!("Got: {}", received); for received in rx { // 迭代器方式 println!("Got: {}", received); } }
-
类似
Rc<RefCell<T>>
达到同一个线程里多个owner对同一个数据的内部修改性,使用Arc<Mutex<T>>
达到多个线程对同一个数据的互斥修改:use std::sync::{Arc, Mutex}; use std::thread; fn main() { // Arc: // atomically reference counted type let counter = Arc::new(Mutex::new(2i64)); 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 += 2; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("result={}", counter.lock().unwrap()); }
-
Trait
std::marker::Send
表示一个类型的 ownership 是否可以安全的转到另一个线程里。- 除了原始指针外,其它基本类型都是
Send
。 - 成员全是
Send
的复合类型也是Send
。 Rc<T>
和RefCell<T>
不是Send
,不能跨线程传递。
- 除了原始指针外,其它基本类型都是
-
Trait
std::marker::Sync
表示一个类型是否可以安全的从多个线程里引用。- 如果
&T
是Send
,那么T
是Sync
。 - 成员全是
Sync
的复合类型也是Sync
。 - 基本类型是
Sync
。 Rc<T>
和RefCell<T>
不是 Sync。
- 如果
17. Object Oriented Programming Features of Rust
- Rust 的 struct 成员默认是私有的,只能通过方法访问,因此 Rust 具备 OOP 里的封装语义;
- Rust 不支持 OOP 风格的继承来重用代码,只能通过 trait 里的默认方法来实现有限的继承语义;
- trait object 使用
dyn SomeTrait
声明,在使用时需要是引用或者智能指针,比如Vec<Box<dyn Error>>
。 - trait object 用来实现 OOP 的运行时多态,采用 dymaic dispatch,也即运行时才能知道调用哪个方法。
- trait object 需要 trait 满足 object safety 约束,最重要的两条:
- trait 里的方法不能返回
Self
类型,比如dyn Clone
就不能作为 trait object 使用 - trait 里不能有泛型参数
- trait 里的方法不能返回
18. Patterns and Matching
-
可以使用模式匹配的地方:
match VALUE { PATTERN => EXPRESSION, PATTERN => EXPRESSION, PATTERN => EXPRESSION, } if let PATTERN = EXPRESSION { } while let PATTERN = EXPRESSION { } for PATTERN in EXPRESSION { } let PATTERN = EXPRESSION; fn print_coordinates(&(x, y): &(i32, i32)) { println!("Current location: ({}, {})", x, y); }
-
PATTERN 的语法
P1 | P2 表示「或」 1..=5 表示闭区间,只适用于整数和char let Point {x: a, y: b} = Point {x: 1, y: 2}; let Point {x, y} = Point {x: 1, y: 2}; _ 忽略 _x 绑定,忽略未使用变量 .. 忽略剩余部分 // match guard match VALUE { PATTERN if CONDITION => EXPRESSION, } // 使用 @ 在匹配时赋值 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) }, }
19. Advanced Features
-
unsafe
可以获得额外的能力:- Dereference a raw pointer
- Call an unsafe function or method
- Access or modify a mutable static variable
- Implement an unsafe trait
- Access fields of
union
s
-
trait 关联类型,默认泛型参数,操作符重载:
use std::ops::Add; // std::ops 中的操作符可以重载 /* trait Add<RHS=Self> { // 默认泛型参数 type Output; // 关联类型 fn add(self, rhs: RHS) -> Self::Output; } */ #[derive(Debug, PartialEq)] struct Point { x: i32, y: i32, } impl Add for Point { type Output = Point; fn add(self, other: Point) -> Point { Point { x: self.x + other.x, y: self.y + other.y, } } }
-
使用方法全名来区分 struct 以及实现的 trait 的同名函数
struct Dog; trait Animal { ... } let dog = Dog; dog.method() // 调用 Dog struct 上直接 impl 的方法 fn method(&self) Animal::method(&dog) // 调用 Dog struct 上 impl Animal 的方法 fn method(&self) Dog::func() // 调用 Dog struct 上直接 impl 的函数 fn func() <Dog as Animal>::func() // 调用 Dog struct 上 impl Animal 的函数 fn func()
-
supertrait
trait B: A { ... } // 实现了 trait B 的 struct 必须也要实现 trait A
-
impl Trait on Type
只有当 Trait 和 Type 至少有一个是当前 crate 定义的时才被允许,如果 Trait 和 Type 都是外部 crate 的,要想扩展 Type 则需要使用 newtype 模式,也就在包装的 tuple struct 类型上实现 trait,Rust 编译器在编译时会自动消除这层包装。如果想要 wrapper 类型包含目标类型的所有方法,则可以实现Deref
trait。use std::fmt; struct Wrapper(Vec<String>); impl fmt::Display for Wrapper { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[{}]", self.0.join(", ")) } } fn main() { let w = Wrapper(vec![String::from("hello"), String::from("world")]); println!("w = {}", w); }
-
type alias:
type Result<T> = std::result::Result<T, std::io::Error>;
-
never type:
!
表示 empty type,比如在 match 的某个分支里使用continue
或者panic!
,这个分支的返回值类型就是!
,比如在函数里有无限循环loop { ... }
,此函数的返回值类型就是!
。 -
trait
Sized
表示编译时知道类型大小,特殊语法?Sized
表示可能知道也可能不知道类型大小。fn foo<T>(t: T) {} // 相当于 fn foo<T: Sized>(t: T) {} fn bar<T: ?Sized>(t: &T) {} // 由于 T 的大小未知,所以用 &T 类型作为参数,也可以用 Box<T>
-
function pointer:
fn(n: i32) -> i32
,可以省略参数名字,function pointer 实现了 closure traitFn
,FnMut
,FnOnce
,所以在以函数作为参数时,推荐使用 closure trait 以支持 function pointer 和 closure 两种。但在与 C 语言交互时,只能使用 function pointer,因为 C 语言不支持 closure。fn foo(f: impl Fn(i32) -> i32, n: i32) -> i32 { f(n) } // 返回 closure: // 第一种返回 trait object,允许函数里不同分支返回不同具体类型的closure 或者函数指针; // 第二种和第三种类似,只能返回一种具体类型 // 由于 Box 实现了 Deref trait,所以使用上都可以写 foo()(12) fn foo() -> Box<dyn Fn(i32) -> i32> { Box::new(|x| x + x) } fn foo() -> impl Fn(i32) -> i32 { Box::new(|x| x + x) } fn foo() -> impl Fn(i32) -> i32 { |x| x + x }
-
declarative macro, aka macro by example, aka pattern macro
-
procedural macro, aka syntax extension, aka compiler plugin
- custom
#[derive]
macro - attribute-like macro
- function-like macro
- custom
Cheat sheets: