rust手册

民间rustlings题解

2023年10月14日

所有权

读者写者问题

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut s = String::from("hello");

let r1 = &s; // 没问题
let r2 = &s; // 没问题
println!("{} and {}", r1, r2);
// 此位置之后 r1 和 r2 不再使用

let r3 = &mut s; // 没问题
println!("{}", r3);
}

解引用

在Rust中,解引用(Dereferencing)是获取一个引用所指向的值的操作。

例如:

1
2
3
4
let x = 5;
let y = &x; // y是一个指向x的引用

let z = *y; // 解引用,z获取到了x的值5

这里y是一个指向x的引用,它的类型是&i32*y表示对y进行解引用,访问它所指向的x的值。

简单来说,解引用的作用就是:

  • 访问引用变量实际保存的值
  • 将引用转换为值类型

另一个常见的例子是解引用智能指针:

1
2
3
let x = Box::new(5); // x是一个Box智能指针
let y = &x; // y是一个指向x的引用
let z = *y; // 解引用,z得到了Box中的数据5

一些需要注意的地方:

  • 只有当引用和指针是可变的(mutable)时才能解引用为一个可变绑定。
  • 多重引用需要多次解引用:let x = & & 5; 需要 **x 来获取值5。
  • 解引用一个空指针会导致程序panic。

所以解引用是Rust中安全访问指针和引用的重要操作。正确理解解引用可以帮助我们更好地使用Rust中的引用与指针。

总结:

取地址&、解引用*和C语言的指针并无区别,用法基本相同

数据类型

结构体

基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
fn main() {
let mut user1 = User {
active: true,
username: String::from("someusername123"),
email: String::from("someone@example.com"),
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
}

类单元结构体

1
2
struct Millionaire;
let m = Millionaire; // m表示百万富翁

unit-like struct表示一个类似于()空元组的结构体,它没有任何字段。

关联函数

  • 所有在impl中的函数

  • 这些函数的第一个参数可以不是&self,比如String::from

    例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #[derive(Debug)]
    struct Rectangle {
    width: u32,
    height: u32,
    }

    impl Rectangle {
    fn square(size: u32) -> Rectangle {
    Rectangle {
    width: size,
    height: size,
    }
    }
    }

    fn main() {
    let sq = Rectangle::square(3);
    }
  • 调用:使用结构体名和 :: 语法来调用这个关联函数:比如 let sq = Rectangle::square(3);。这个方法位于结构体的命名空间中::: 语法用于关联函数和模块创建的命名空间。

  • 一个结构体可以有多个impl

2023年10月15日

枚举

定义

枚举出的可以是类型,整个枚举也是类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#![allow(unused)]
fn main() {
enum IpAddrKind {
V4,
V6,
}

struct IpAddr {
kind: IpAddrKind,
address: String,
}

let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
}

函数传参中:

1
2
3
4
fn route(ip_type: IpAddrKind) { }
// 调用
route(IpAddrKind::V4);
route(IpAddrKind::V6);

可以给枚举值关联数据类型

1
2
3
4
5
6
7
8
9
#![allow(unused)]
fn main() {
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
}

每个成员可以处理不同类型和数量的数据:

1
2
3
4
5
6
7
8
9
#![allow(unused)]
fn main() {
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
}

成员可以是各种类型:

1
2
3
4
5
6
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}

枚举上也可以定义方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#![allow(unused)]
fn main() {
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}

impl Message {
fn call(&self) {
// 在这里定义方法体
}
}
let m = Message::Write(String::from("hello"));
m.call();

options

它表示一个值可能存在或不存在,相当于对NULL进行了封装

样例:

1
2
3
4
5
6
7
8
9
10
#![allow(unused)]
fn main() {
enum Option<T> {
Some(T),
None,
}
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
}

match

enmu:

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
27
28
29
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
// --snip--
}

enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}

fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
}
}
}

fn main() {
value_in_cents(Coin::Quarter(UsState::Alaska));
}

options

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn reroll() {}
}

ref取消match对变量的所有权

1
2
3
4
5
6
let y: Option<Point> = Some(Point { x: 100, y: 200 });

match y {
Some(ref p) => println!("Co-ordinates are {},{} ", p.x, p.y),
_ => panic!("no match!"),
}

if let

match:

1
2
3
4
5
let mut count = 0;
match coin {
Coin::Quarter(state) => println!("State quarter from {:?}!", state),
_ => count += 1,
}

if let

1
2
3
4
5
6
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {:?}!", state);
} else {
count += 1;
}

两者完全等价,if let可用于optionsenmu

Vec 的 pop 函数会套一层 Option

while let

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
// 将 `optional` 设为 `Option<i32>` 类型
let mut optional = Some(0);

// 这读作:当 `let` 将 `optional` 解构成 `Some(i)` 时,就
// 执行语句块(`{}`)。否则就 `break`。
while let Some(i) = optional {
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
// ^ 使用的缩进更少,并且不用显式地处理失败情况。
}
// ^ `if let` 有可选的 `else`/`else if` 分句,
// 而 `while let` 没有。
}

样例

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
enum Message {
// TODO: implement the message variant types based on their usage below
ChangeColor(u8,u8,u8),
Echo(String),
Move(Point),
Quit,
}

struct Point {
x: u8,
y: u8,
}

struct State {
color: (u8, u8, u8),
position: Point,
quit: bool,
message: String
}

impl State {
fn change_color(&mut self, color: (u8, u8, u8)) {
self.color = color;
}

fn quit(&mut self) {
self.quit = true;
}

fn echo(&mut self, s: String) { self.message = s }

fn move_position(&mut self, p: Point) {
self.position = p;
}
fn process(&mut self, message: Message) {
match message{
Message::ChangeColor(a,b,c) => self.change_color((a,b,c)),
Message::Echo(s)=>self.message=s,
Message::Move(p)=>self.move_position(p),
Message::Quit=>self.quit(),
}
}
}

fn main(){
let mut state = State {
quit: false,
position: Point { x: 0, y: 0 },
color: (0, 0, 0),
message: "hello world".to_string(),
};
state.process(Message::ChangeColor(255, 0, 255));
state.process(Message::Echo(String::from("hello world")));
state.process(Message::Move(Point { x: 10, y: 15 }));
state.process(Message::Quit);
}

字符串

有三种(两种)

  • let hello = String::from("hello");:类型为String
  • let he = &hello[..2];:类型为&str
  • let he="he";:类型为&str

拼接:

1
2
3
let s1=String::from("Hello, ");
let s2=String::from("world!");
let s3=s1+&s2;// 注意 s1 被移动了,不能继续使用
1
2
3
fn compose_me(input: &str) -> String {
format!("{} world!", input)
}

trim:

1
2
3
let mut string=String::from("  ,aaaaa  ");
let chars_to_trim: &[char] = &[' ', ','];
let trimmed_str: &str=string.trim_matches(chars_to_trim);
1
2
3
fn trim_me(input: &str) -> String {
input.trim().to_string()
}
  • trim返回的是&str,再调用to_string可转为String

替换:

1
2
3
4
// 堆分配一个字符串
let alice = String::from("I like dogs");
// 分配新内存并存储修改过的字符串
let bob: String = alice.replace("dog", "cat");

转换

  • &str到String:在不考虑格式化转换或往返一个Vec或[u8]的情况下,至少存在这么多种,String::from(),to_string(),to_owned(),into(),format!
  • String到&str:as_str(),as_ref(),Deref<Target=str>,&x[..]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// rustlings string4
fn string_slice(arg: &str) {
println!("{}", arg);
}
fn string(arg: String) {
println!("{}", arg);
}

fn main() {
string_slice("blue");
string("red".to_string());
string(String::from("hi"));
string("rust is fun!".to_owned());
string("nice weather".into());
string(format!("Interpolation {}", "Station"));
string_slice(&String::from("abc")[0..1]);
string_slice(" hello there ".trim());
string("Happy Monday!".to_string().replace("Mon", "Tues"));
string("mY sHiFt KeY iS sTiCkY".to_lowercase());
}

转大写:to_uppercase()

在向量中这样使用:

1
2
3
4
fn main() {
let mut shopping_list: Vec<&str> = Vec::new();
shopping_list.push("milk");
}

hashmap

样例:

1
2
3
4
5
6
7
8
9
10
// 引入
use std::collections::HashMap;
// 初始化
let mut scores = HashMap::new();
// 插入
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
// 导出
let team_name = String::from("Blue");
let score = scores.get(&team_name).copied().unwrap_or(0);

查看hashmap中是否有键,返回布尔值

1
basket.get(&fruit).is_some()

返回数值,不存在则返回0:

1
basket.get(&fruit).copied().unwrap_or(0)

or_insert 方法在键对应的值存在时就返回这个值的可变引用,如果不存在则将参数作为新值插入并返回新值的可变引用

1
2
3
4
5
6
7
let team_1_name = v[0].to_string();        
let g = scores.entry(team_1_name).or_insert(
Team {
goals_scored: 0,
goals_conceded: 0,
},
);

Vector

创建:

1
let v: Vec<i32> = Vec::new();
1
2
3
4
5
let v: Vec<i32> = vec![];

let v = vec![1, 2, 3, 4, 5];

let v = vec![0; 10]; // 十个零

增删:

1
2
v.push(3);
let two = v.pop();

函数传参:

1
2
3
4
5
fn array_and_vec() -> ([i32; 4], Vec<i32>) {
let a = [10, 20, 30, 40]; // a plain array
let v = vec![10, 20, 30, 40];// TODO: declare your vector here with the macro for vectors
(a, v)
}

包和模块

参考:https://course.rs/basic/crate-module

模块

代码可见性pub

包和函数默认都是私密的,想要从外部访问,必须加上pub关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pub mod sausage_factory {
// Don't let anybody outside of this module see this!
fn get_secret_recipe() -> String {
String::from("Ginger")
}

pub fn make_sausage() {
get_secret_recipe();
println!("sausage!");
}
}

fn main() {
sausage_factory::make_sausage();
}

同一个模块同一层级下,不必考虑可见性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mod delicious_snacks {
// TODO: Fix these use statements
pub use self::fruits::PEAR as fruit;
pub use self::veggies::CUCUMBER as veggie;

mod fruits {
pub const PEAR: &'static str = "Pear";
pub const APPLE: &'static str = "Apple";
}

mod veggies {
pub const CUCUMBER: &'static str = "Cucumber";
pub const CARROT: &'static str = "Carrot";
}
}

错误处理

panic!解析

在不可恢复的错误处调用,调用后会直接退出

Result和?

Result是自带的一个枚举类型

1
2
3
4
enum Result<T, E> {
Ok(T),
Err(E),
}

读取文件:

1
2
3
4
5
6
7
8
9
10
11
12
use std::fs::File;

fn main() {
let f = File::open("hello.txt");

let f = match f {
Ok(file) => file,
Err(error) => {
panic!("Problem opening the file: {:?}", error)
},
};
}
1
2
3
4
5
6
7
8
9
pub fn generate_nametag_text(name: String) -> Result<String,String> {
if name.is_empty() {
// Empty names aren't allowed.
// None
Err("`name` was empty; it must be nonempty.".to_string())
} else {
Ok(format!("Hi! My name is {}", name))
}
}

关于?

  • 如果 Result 的值是 Ok,这个表达式将会返回 Ok 中的值而程序将继续执行。

  • 如果值是 ErrErr 将作为整个函数的返回值,就好像使用了 return 关键字一样,这样错误值就被传播给了调用者。

关于unwrap

  • 如果 Result 值是成员 Okunwrap 会返回 Ok 中的值。
  • 如果 Result 是成员 Errunwrap 会为我们调用 panic!不会有返回值

泛型、Trait

泛型

使用:

1
2
3
4
5
6
7
8
9
struct Wrapper <T>{
value: T,
}

impl<T> Wrapper<T> {
pub fn new(value: T) -> Wrapper<T> {
Wrapper { value }
}
}

trait

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
trait AppendBar {
fn append_bar(self) -> Self;
}

impl AppendBar for String {
// TODO: Implement `AppendBar` for type `String`.
fn append_bar(self) -> Self {
format!("{}Bar", self)
}
}

fn main() {
let s = String::from("Foo");
let s = s.append_bar();
println!("s: {}", s);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct Post {
pub title: String, // 标题
pub author: String, // 作者
pub content: String, // 内容
}

impl Summary for Post {
fn summarize(&self) -> String {
format!("文章{}, 作者是{}", self.title, self.author)
}
}
fn main() {
let weibo = Weibo{username: "sunface".to_string(),content: "好像微博没Tweet好用".to_string()};
println!("{}",post.summarize());
}

针对vec添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
trait AppendBar {
fn append_bar(self) -> Self;
}

// TODO: Implement trait `AppendBar` for a vector of strings.
impl AppendBar for Vec<String>{
fn append_bar(mut self)->Self{
self.push("Bar".to_string());
self
}
}

let mut foo = vec![String::from("Foo")].append_bar();

使用特征作为参数

1
2
3
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}

item必须实现了Summary这个特征

特征约束

多重约束

1
2
3
fn some_func(item: impl SomeTrait + OtherTrait) -> bool {
item.some_function() && item.other_function()
}

生命周期

1

只是为了糊弄编译器

1
2
3
4
5
6
7
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
  • 和泛型一样,使用生命周期参数,需要先声明 <'a>
  • xy 和返回值至少活得和 'a 一样久(因为返回值要么是 x,要么是 y)
  • 'a的定义:'z的大小就是x和y的作用域的重合部分,也就是比较短命的那个
  • 因此,返回值的生命周期也是x和y中作用域较小的那个

解决悬垂指针

错误的:

1
2
3
4
fn longest<'a>(x: &str, y: &str) -> &'a str {
let result = String::from("really long string");
result.as_str()
}

result 在函数结束后就被释放,但是在函数结束后,对 result 的引用依然在继续

正确的:

1
2
3
4
5
6
7
fn longest<'a>(_x: &str, _y: &str) -> String {
String::from("really long string")
}

fn main() {
let s = longest("not", "important");
}

返回内部字符串的所有权,然后把字符串的所有权转移给调用者

结构体的生命周期

1
2
3
4
5
6
7
8
9
10
11
12
struct Book<'a> {
author: &'a str,
title: &'a str,
}

fn main() {
let name = String::from("Jill Smith");
let title = String::from("Fish Flying");
let book = Book { author: &name, title: &title };

println!("{} by {}", book.title, book.author);
}

结构体引用的字符串必须要比结构体活得长

安全性保证

自动化测试

1
2
3
4
5
6
7
8
#[cfg(test)]
mod tests {
#[test] // 测试函数需要 `test` 属性进行标注,使用它可以派生自动实现的Debug、Copy等特征
#[should_panic] // 若panic!了就测试成功,没有就会失败
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

Unsafe Rust

使用 unsafe 非常简单,只需要将对应的代码块标记下即可:

1
2
3
4
5
6
7
8
9
fn main() {
let mut num = 5;

let r1 = &num as *const i32;

unsafe {
println!("r1 is: {}", *r1);
}
}

上面代码中, r1 是一个裸指针(raw pointer),由于它具有破坏 Rust 内存安全的潜力,因此只能在 unsafe 代码块中使用,如果你去掉 unsafe {},编译器会立刻报错。

作用:

  • 解引用裸指针,就如上例所示
  • 调用一个 unsafe 或外部的函数
  • 访问或修改一个可变的静态变量
  • 实现一个 unsafe 特征
  • 访问 union 中的字段

闭包与迭代

迭代

  • into_iter 会夺走所有权
  • iter 是借用
  • iter_mut 是可变借用
1
2
let mut my_iterable_fav_fruits = my_fav_fruits.iter();   
assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
1
2
let mut my_iterable_fav_fruits = my_fav_fruits.into_iter();   
assert_eq!(my_iterable_fav_fruits.next(), Some("banana"));

可用方法

filter

1
2
3
for num in (0..=100).filter(|x| x % 3 == 0) {
eprint!("{} ", num);
}

map

1
2
3
4
let vec=vec![1,2,3,4,5];
for num_str in vec.iter().map(|x|x.to_string()){
eprint!("{}",num_str);
}

collect

1
let vec = (0..=100).collect::<Vec<_>>();//Vec的泛型参数可以不写,由编译器推导为i32.

rev

逆序遍历

1
2
3
4
for i in (0..=100).rev() {
eprint!("{} ", i);
}
// 100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 ...

max

max是求迭代元素的最大值,比较简单不多说,给个例子。

1
2
3
let vec = vec![1, 5, 3, 4, 2];
let max = vec.iter().max().unwrap();
eprint!("{}", max);//输出5

sum

sum是求迭代元素的和,需要指定一下结果的类型。

1
2
3
let vec = vec![1, 2, 3, 4, 5];
let sum = vec.iter().sum::<i32>();
eprint!("{}", sum);//输出15

fold

类似js的reduce

1
2
3
let vec = vec![1, 2, 3, 4, 5];
let res = vec.iter().fold(0, |acc, x| acc + x);
eprint!("{}", res);

求阶乘:

1
(1..num + 1).fold(1, |acc, x| acc * x)

scan

scan和fold很类似,但是它允许你直接修改累计值,并且允许你选择什么时候停止迭代,取决于你传入的闭包何时返回None。比如我不仅要求数组的和,还要获取每次累加的结果,就可以这么写。

1
2
3
4
5
6
7
let vec = vec![1, 2, 3, 4, 5];
for step in vec.iter().scan(0, |acc, x| {
*acc+= *x;
Some(*acc)
}) {
eprint!("{} ", step);
}//打印1 3 6 10 15

智能指针

Box

Rc Arc

Rc单线程,Arc多线程

都是只读

Rc

1
2
3
4
5
6
7
8
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::new(var):创建一个智能指针并返回
  • Rc::strong_count():查询引用计数
  • Rc::clone():克隆智能指针,并增加引用计数

Arc

1
2
3
4
5
6
7
8
9
10
11
12
use std::sync::Arc;
use std::thread;

fn main() {
let s = Arc::new(String::from("多线程漫游者"));
for _ in 0..10 {
let s = Arc::clone(&s);
let handle = thread::spawn(move || {
println!("{}", s)
});
}
}

Cow

  • Cow::Borrowed(&'a T): 表示一个不可变的引用,可以用于读取数据;
  • Cow::Owned(T): 表示一个可变的数据,可以用于修改数据。
  • Cow::from():根据数据类型的不同,返回一个 Cow::Borrowed 或 Cow::Owned 类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn main() {
let name = "Tom".to_string();
let species = "Cat".to_string();

let animal = Animal {
name: Cow::from(&name),
age: 3,
species: Cow::from(species),
};

println!("Name: {}", animal.name);
println!("Species: {}", animal.species);
}
// 输出结果:
// Name: Tom
// Species: Cat

多线程

样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use std::thread;
use std::time::{Duration, Instant};

fn main() {
let handle = thread::spawn(|| {
let start = Instant::now();
println!("开始睡觉...");
thread::sleep(Duration::from_millis(250));
start.elapsed().as_millis()
});
println!("我在thread::spawn下面");
let duration = handle.join().unwrap();
println!("睡醒啦!!!睡了{}ms",duration);
}

修改数据

使用Arc

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
use std::sync::{Mutex,Arc};
use std::thread;
use std::time::Duration;

struct JobStatus {
jobs_completed: u32,
}

fn main() {
let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
let mut handles = vec![];
for _ in 0..10 {
let status_shared = Arc::clone(&status);
let handle = thread::spawn(move || {
thread::sleep(Duration::from_millis(250));
let mut num = status_shared.lock().unwrap();
num.jobs_completed += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
let mut num = status.lock().unwrap();
println!("jobs completed {}", num.jobs_completed);
}
}

mpsc库

在 Rust 中,mpsc::channel 的作用是创建一个多生产者(mpsc)通道,用于线程之间发送消息。

mpsc代表 multiple producer, single consumer(多生产者,单消费者)。

主要作用有:

  1. 在线程间建立消息通道

    mpsc::channel 返回一个 transmitter 和 receiver, transmitter 可以通过通道发送消息,receiver 可以接收消息。

  2. 发送端和接收端可在不同线程

    transmitter 和 receiver 可以移动到不同的线程,这样可以在线程间传递消息。

  3. 提供非阻塞的异步消息传递

    如果通道已满,发送会立即返回错误,不会阻塞线程。

示例:

1
2
3
4
5
6
7
8
9
10
use std::thread;
use std::sync::mpsc;

let (tx, rx) = mpsc::channel();

thread::spawn(move || {
tx.send(10).unwrap();
});

let message = rx.recv().unwrap();

所以 mpsc 通道非常适合在 Rust 中低开销地在线程间传递消息。

声明式宏

macro_rules

  • 对于声明宏(declarative macro),必须要先定义后使用。

    声明宏主要包括自定义派生(derive)和属性宏(attribute macro)。这类宏需要在编译前定义,所以必须先定义后调用。

  • 对于过程宏(procedural macro),则可以先调用后定义。

使模块中的宏可以在当前模块可用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#[macro_use]
mod macros {
macro_rules! my_macro {
() => {
println!("Check out my macro!");
}; // 要加逗号
($val:expr) => {
println!("Look at this other macro: {}", $val);
}
}
}

fn main() {
my_macro!();
}

过程宏

三种:

  • 自定义 derive
  • 属性宏
  • 函数宏

rust手册
https://asxjdb.github.io/2023/10/21/rust手册/
作者
Asxjdb
发布于
2023年10月21日
许可协议