Rust 语法学习
变量与可变性
变量声明使用let
let a = 3; // 类型推断
let a: u32; // 声明类型
Rust语言变量默认赋值后不可变,如果要重复使用,需要加mut声明可变
let mut a = 3;
a = 5;
常量
使用const
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
基础数据类型
整型
| 长度 | 有符号 | 无符号 |
|---|---|---|
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| 根据机器 32位 64位 | isize | usize |
| 字面值 | 例子 |
|---|---|
| 十进制 | 1_000(可使用 _ 方便可读) |
| 十六进制 | 0xff |
| 八进制 | 0o77 |
| 二进制 | 0b111_1000 |
| Byte(单字节字符,仅限u8) | b'A' |
浮点型
| 精度 | 关键字 |
|---|---|
| 单精度 | f32 |
| 双精度 | f64 |
字符
let c: char = 'Z';
字符串(切片)
let s: &str = "abcd";
布尔
let b: bool = false;
元组
let tuple = (1, 1.11, "abc");
// 元组声明类型
let tuple_type: (u32, f32, &str) = (1, 1.11, "abc");
// 访问元组元素
println!("{} {} {}", tuple.0, tuple.1, tuple.2);
数组
let arr = [0; 4]; // 长度为4,全是0的数组
let arr_same = [0, 0, 0, 0];
// 数组声明和长度
let arr_type: [u8; 4];
arr_type = [0, 0, 0, 0];
函数
无参函数
fn func_name() {
// body
}
带参
fn func_name(x: u32) {
println!("x: {}", x);
}
返回值
fn five() -> u8 {
5
}
控制流
if else
let number = 4;
if number < 5 {
println!("{} less than 5", number);
} else if number > 3 {
println!("{} bigger than 3", number);
} else {
println!("{} equals 4", number);
}
let if
let x = if number == 1 { "male" } else { "female" };
loop
let mut count = 0;
loop {
println!("Again!");
count += 1;
if count == 3 {
break;
}
}
// loop 打 tag ,并使用 break 退出
let mut count_2 = 2;
'loopTag: loop {
let mut remaining = 3;
loop {
remaining -= 1;
if remaining = 0 {
break;
}
if count_5 = 0 {
break 'loopTag; // 退出外层循环
}
}
count_2 -= 1;
}
// break 返回值
let mut count = 0;
let result = loop {
count += 1;
if count == 20 {
break count * 2;
}
}
while
let mut count = 0;
while count < 10 {
count += 1;
}
for
let a = [1,2,3,4,5];
for element in a {
println!("the value is: {}", element);
}
所有权
相关解释
任何基本类型的组合可以
Copy,不需要分配内存或某种形式资源的类型是可以Copy的。如下是一些Copy的类型:
- 所有整数类型,比如
u32。- 布尔类型,
bool,它的值是true和false。- 所有浮点数类型,比如
f64。- 字符类型,
char。- 元组,当且仅当其包含的类型也都是
Copy的时候。比如,(i32, i32)是Copy的,但(i32, String)就不是。- 数组同理上条
- 引用类型,例如转移所有权中的最后一个例子
代码演示
- 存在【堆】中的数据的变量离开作用域后,会丢失所有权
let s1 = String::from("some string");
let s2 = s1;
println!("s1: {}", s1); // 报错:borrow of moved value: `s1`。 原因:s1所有权被移动到s2
- 可以通过返回值的方式将值的所有权返回
/// 带走所有权
fn takes_ownership(string: String) {
println!("{}", string);
// 所有权未返回,rust调用了它的drop方法
// 占用的内存被释放
}
let s = String::from("fn str");
takes_ownership(s);
// println!("{}", s); // value borrowed here after move; s所有权被带走
引用、解引用、借用
引用和解引用
let x = 5;
let y = &y;
assert_eq!(5, x);
assert_eq!(5, *y); // *y 解引用
不可变引用
fn calculate_length(s: &String) -> usize {
// s.push_str("yes"); // cannot borrow `*s` as mutable, as it is behind a `&` reference; 借用的变量无法修改,它是只读的
s.len()
}
let s1 = String::from("hello");
let len = calculate_length(&s1); // 借用s1地址
println!("The length of '{}' is {}.", s1, len);
可变引用
fn change(some_string: &mut String) { // 0.必须声明接受可变
some_string.push_str(", world");
}
let mut str1 = String::from("hello"); // 1.声明一个可变变量
change(&mut str1); // 2.传入可变的引用
限制
- 同一时刻,你只能拥有要么一个可变引用,要么任意多个不可变引用
- 引用必须总是有效的
切片
切片是一类引用,所以它没有所有权。
// 字符串切片
let str1 = String::from("Hello world");
let hello = &str1[0..5];
let world = &str1[6..11];
// 数组切片
let arr = [0; 10];
let slice_0 = &arr[1..4]; // 下标1到3
let slice_1 = &arr[1..=3]; // 下标1到3
结构体
基本声明方式
// 声明方式
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
// 从其他实例创建实例
let user1 = User {
active: true,
username: String::from("An"),
email: String::from("an@example.com"),
sign_in_count: 5,
};
let mut user2 = User {
email: String::from("user4@example.com"),
..user1 // 其他属性从user1迁移过来,user1被迁移的属性不再属于user1
};
元组声明结构体
struct Color(u8, u8, u8);
// 赋值
let black = Color(0, 0, 0);
// 取值
println!("r{}, g{}, b{}", black.0, black.1, black.2);
结构体方法
struct Rectangle {
width : u32,
height : u32,
}
impl Rectangle {
// &self 指代结构体实例
fn area(&self) -> u32 {
self.width * self.height
}
// 带参数
fn width_is_less(&self, rect: &Rectangle) -> bool {
self.width < rect.width
}
}
// 可以实现多个Rectangle支持的方法
impl Rectangle {
fn max(&self) -> u32 {
if self.width > self.height {
return self.width;
}
self.height
}
}
枚举
// 基本声明方式
enum IpAddrKind {
V4,
V6,
}
let my_kind = IpAddrKind::V4;
// 包含具体值的枚举
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
枚举定义方法
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
println!("Call me, maybe.");
}
}
let m = Message::Write(String::from("HELLO"));
m.call();
Option 枚举
// T泛型
// 内置的Option枚举
enum Option<T> {
None,
Some(T),
}
let some_number = Some(5); // Option枚举值的使用
let some_string = Some("a string");
let absent_number: Option<i32> = None;
// Option 可以使用 match语法,进行模式匹配
// 可使用 unwrap 获取Some值
match语法
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
/// 根据Coin的枚举值,返回对应价值
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
let value = value_in_cents(Coin::Penny);
println!("Coin::Penny = {}", value);
_ 占位符和通配模式
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_play(num_spaces: u8) {}
fn reroll() {}
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
other => move_play(other),
}
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(), // _ 通配符表示用不上匹配这值的参数
}
if let语法
场景:只想匹配某个值,用match显得比较啰嗦
let config_max = Some(3u8); // 3u8,u8标识类型
match config_max {
Some(max) => println!("The maximum is configured to be {}", max),
_ => (),
}
// 使用if let语法
if let Some(max) = config_max {
println!("The maximum is configured to be {}", max);
}
错误
panic 无法恢复的错误
// 使用 panic! 宏生成一个崩溃
// panic!("crash and burn");
let v = vec![1, 2, 3];
v[99]; // 尝试访问长度以外的值
// 可使用
// RUST_BACKTRACE=1 cargo run
// 来追踪调用栈
Result枚举,可恢复的错误
// 内置的Result枚举
enum Result<T, E> {
Ok(T),
Err(E),
}
let path = "hello.txt"; // 假设存在
// let path = "hello.txt1"; // 如果不存在 thread 'main' panicked at 'Problem opening the file: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/result.rs 如果文件不存在
let f = match File::open(path) {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
// 可使用 ? 快速获取ok的值,否则panic
泛型
结构体中使用泛型
// 相同类型
struct Point<T> {
x: T,
y: T,
}
let integer = Point { x: 10, y: 20 };
let float = Point { x: 1.1, y: 2.2 };
// 不同类型
struct PersonInfo<T, N> {
name: T,
age: N,
}
let leo = PersonInfo {
name: "Leo",
age: 19,
};
结构体方法中使用泛型
struct Point<T> {
x: T,
y: T,
}
// 注意语法 impl<T>
impl<T> Point<T> {
/// 取得 x 值
fn x(&self) -> &T {
&self.x
}
// Self 指代结构体类型的关键字
fn new(x:T, y:T) -> Self {
Self {
x, y
}
}
}
/// 只有 Point 的泛型为f32时,才拥有此方法
impl Point<f32> {
/// 这个点到原点(0,0)的距离
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
let p = Point { x: 10.2, y: 20.3 };
println!("p.x = {}", p.x());
println!("distance from origin is {}", p.distance_from_origin());
枚举中使用泛型
enum Dream<T, E> {
Want(T),
Nil(E),
}
let a: Dream<i32, &str> = Dream::Want(1);
if let Dream::Want(x) = a {
println!("Want: {}", x);
}
trait(类似接口)
// trait中所有的方法,都需要被实现
pub trait Summary {
/// trait 声明要实现的方法
fn summarize(&self) -> String;
/* 有默认实现的方法
fn summarize_default_impl(&self) -> String {
String::from("(No more)")
}
*/
}
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
// 为结构体NewsArticle实现Summary的方法
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
补充:
- 需要引用trait,才可以调用其实现。
- 如果出现接口同名的情况,可以使用如下方式调用接口方法。
trait Pilot {
fn fly(&self);
}
trait Wizard {
fn fly(&self);
}
struct Human;
impl Pilot for Human {
fn fly(&self) {
println!("This is your captain speaking.");
}
}
impl Wizard for Human {
fn fly(&self) {
println!("Up!");
}
}
impl Human {
fn fly(&self) {
println!("*waving arms furiously*");
}
}
let person = Human;
Pilot::fly(&person); // 调用Pilot trait fly实现
Wizard::fly(&person); // 调用Wizard trait fly实现
person.fly(); // 调用结构体自身方法
// 如果改方法没有&self借用,则需要使用as转换后调用
trait Animal {
fn baby_name() -> String;
}
struct Dog;
impl Dog {
fn baby_name() -> String {
String::from("Spot")
}
}
impl Animal for Dog {
fn baby_name() -> String {
String::from("puppy")
}
}
println!("A baby dog is called a {}", <Dog as Animal>::baby_name()); // 这样才会调用Animal trait的实现
trait 作为参数(多态)
pub trait Display {
fn show(&self) -> String {
String::from("(showing)")
}
}
pub trait Summary {
fn summarize(&self) -> String {
String::from("(No more)")
}
}
struct Item {
name: String
}
// 采用默认实现演示
impl Display for Item{}
impl Summary for Item{}
// 只要实现了Summary都可作为参数
fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
// 语法糖,更适合多个trait
fn notify_sugar<T: Summary>(item: &T) {
println!("Breaking news2! {}", item.summarize());
}
// 通过 + 来约束多个trait实现的参数
fn notify_multi(item: &(impl Summary + Display)) {
println!("{} - {}", item.summarize(), item.show());
}
fn notify_multi_sugar<T: Summary + Display>(item: &T) {
println!("{} - {} - sugar", item.summarize(), item.show());
}
// 通过 where 增加可读性
fn notify_multi_sugar_where<T>(item: &T)
where
T: Summary + Display,
{
println!("{} - {} - sugar - where", item.summarize(), item.show());
}
返回实现了trait的实例(常用于闭包、迭代器)
pub trait Summary {
fn summarize(&self) -> String {
String::from("(No more)")
}
}
struct Item {
name: String
}
// 采用默认实现演示
impl Summary for Item{}
// 返回已实现了Summary trait的结构体实例,不过这只适用于返回单一类型的情况,在闭包和迭代器中非常实用
fn returns_summarizable -> impl Summary {
Item {
name: String::from("Hammer")
}
}
泛型中实现了多个trait的才可调用的方法
struct Pair<T> {
x: T,
y: T,
}
let pair = Pair { x: 1, y: 2 };
// 当T实现了Display和PartialOrd接口才可以调用
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);
}
}
}
pair.cmp_display();
超trait
use std::fmt::Display;
// OutlinePrint使用Display trait的to_string实现
pub trait OutlinePrint: Display {
fn outline_print(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", " ".repeat(len + 2));
println!("* {} *", output);
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}
pub struct Wrapper<T: Display>(pub T);
impl<T: Display> Display for Wrapper<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "{}", self.0)
}
}
impl<T: Display> OutlinePrint for Wrapper<T> {}
let content = Wrapper(String::from("hello world"));
content.outline_print();
生命周期
// 默认rust的变量生命周期在作用域中,标记生命周期后,返回值与参数共享生命周期,才可返回
fn longest(x: &'static str, y: &'static str) -> &'static str {
if x.len() > y.len() {
x
} else {
y
}
}
let x = "123";
let y = "3333";
println!("longest: {}", longest(x, y));
结构体中注解生命周期
struct ImportantExcerpt<'a> {
part: &'a str,
}
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
/*
创建了一个 ImportantExcerpt 的实例,它存放了变量 novel 所拥有的 String 的第一个句子的引用。
novel 的数据在 ImportantExcerpt 实例创建之前就存在。
另外,直到 ImportantExcerpt 离开作用域之后 novel 都不会离开作用域,所以 ImportantExcerpt 实例中的引用是有效的。
*/
闭包
// 基础语法
let expensive_closure = |num| {
println!("Calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
};
// 带类型
let add_one_closure_with_type = |x: u32| -> u32 { x + 1 };
// 闭包作为返回值
pub fn closure_add(step: u32) -> impl FnMut() -> u32 {
let mut x: u32 = 0;
// move关键字
move || {
x += step;
x
}
}
// 闭包作为参数
pub fn get_word<F>(closure: F)
where
F: Fn(&str),
{
let word = "Hello World";
closure(word);
}
迭代器
// 基本语法
let v1 = vec![1, 2, 3];
// iter、iter_mut、into_iter都跟所有权有关系,挑取适合的方法使用
for v in v1.iter() {
println!("item: {}", v);
}
// 迭代器使用闭包
#[derive(PartialEq, Debug)]
struct Shoe {
size: u32,
style: String,
}
fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
shoes
.into_iter()
.filter(|shoe| shoe.size == shoe_size)
.collect() // collect() 触发消费迭代器,获得所有权
}
// 如果要实现自己的迭代器,需要实现Iterator trait
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Self { count: 0 }
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
// 超过5后,不再返回
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
// 使用自定义迭代器中其他 Iterator trait 方法
#[test]
fn using_other_iterator_trait_methods() {
let sum: u32 = Counter::new()
.zip(Counter::new().skip(1)) // 第一个Counter 和 第二个Counter(忽略第一个值)联合起来
.map(|(a, b)| a * b) // 它们的值相乘
.filter(|x| x % 3 == 0) // 只保留能被 3 整除的值
.sum(); // 都加起来
assert_eq!(18, sum);
}
智能指针
Box<T> 将值放堆上,可创建递归结构
box 允许你将一个值放在堆上而不是栈上。留在栈上的则是指向堆数据的指针。
使用场景:
- 当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候
- 当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候
- 当希望拥有一个值并只关心它的类型是否实现了特定 trait 而不是其具体类型的时候
// 基本演示
let a = 5;
let b = Box::new(5);
println!("a == 5 : {}", a == 5); // ture
println!("b : {}", b); // 5
println!("*b == 5 : {}", *b == 5); // *解引用,ture
println!("a == *b : {}", a == *b); // ture
// 一个不使用 box 时无法定义的类型的例子
// Box 允许创建递归类型
enum List {
Cons(i32, Box<List>),
Nil,
}
use crate::List::{Cons, Nil};
let list = Cons(1,Box::new(
Cons(2, Box::new(
Cons(3, Box::new(
Cons(5, Box::new(Nil))))))),
);
// 遍历
let mut current = Box::new(list);
loop {
if let Cons(x, next) = *current {
println!("list: {}", x);
current = next;
} else {
break;
}
}
Deref trait 定义“解引用时候”的返回“其值引用”
返回引用的原因是因为不能发生所有权变更。
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
Self(x)
}
}
impl<T> Deref for MyBox<T> {
// type Target = T; 语法定义了用于此 trait 的关联类型。
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
let x = 5;
let y = MyBox::new(5);
assert_eq!(5, x); // true
assert_eq!(5, *y); // true 底层:*(y.deref())
Drop trait 定义变量出作用域时调用方法
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer { data: String::from("my stuff") };
let d = CustomSmartPointer { data: String::from("other stuff") };
println!("CustomSmartPointers created.");
}
// 输出
// CustomSmartPointers created.
// Dropping CustomSmartPointer with data `other stuff`!
// Dropping CustomSmartPointer with data `my stuff`!
Rc 单线程 - 可多重引用的智能指针
只使用Rc,其内容只读
引用的时候使用Rc::Clone方法,计数。当数为0才会被清理。
enum ListMultiRef {
Cons(i32, Rc<ListMultiRef>),
Nil,
}
let a = Rc::new(ListMultiRef::Cons(10, Rc::new(ListMultiRef::Nil)));
let b = ListMultiRef::Cons(2, Rc::clone(&a));
let c = ListMultiRef::Cons(3, Rc::clone(&a));
// Rc::strong_count 查看计数
println!("&a Rc count: {}", Rc::strong_count(&a));
RefCell 内部可变性的用武之地
当不可变引用,需要可变其属性的时候,可以使用。
pub trait Messenger {
// 注意 Messenger 签名 &self 是不可变借用
fn send(&self, msg: &str);
}
pub struct LimitTracker<'a, T: Messenger> {
messenger: &'a T,
value: usize,
max: usize,
}
impl<'a, T> LimitTracker<'a, T>
where T: Messenger {
pub fn new(messenger: &T, max: usize) -> LimitTracker<T> {
LimitTracker {
messenger,
value: 0,
max,
}
}
pub fn set_value(&mut self, value: usize) {
self.value = value;
let percentage_of_max = self.value as f64 / self.max as f64;
if percentage_of_max >= 1.0 {
self.messenger.send("Error: You are over your quota!");
} else if percentage_of_max >= 0.9 {
self.messenger.send("Urgent warning: You've used up over 90% of your quota!");
} else if percentage_of_max >= 0.75 {
self.messenger.send("Warning: You've used up over 75% of your quota!");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
struct MockMessenger {
sent_messages: RefCell<Vec<String>>,
}
impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger { sent_messages: RefCell::new(vec![]) }
}
}
impl Messenger for MockMessenger {
fn send(&self, message: &str) {
// 注意:borrow_mut 使用了可变借用(补充:borrow 能获取其不可变借用)
// self 是不可变的借用
self.sent_messages.borrow_mut().push(String::from(message));
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
// --snip--
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
limit_tracker.set_value(75);
assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
}
}
fn main() {}
结合 Rc<T> 和 RefCell<T> 来拥有多个可变数据所有者
RefCell<T> 的一个常见用法是与 Rc<T> 结合。回忆一下 Rc<T> 允许对相同数据有多个所有者,不过只能提供数据的不可变访问。如果有一个储存了 RefCell<T> 的 Rc<T> 的话,就可以得到有多个所有者 并且 可以修改的值。
#[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
let value = Rc::new(RefCell::new(5));
let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(6)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(10)), Rc::clone(&a));
// * 解了Rc引用 -> RefCell 可变借用,修改值
*value.borrow_mut() += 10;
println!("a after = {:?}", a);
println!("b after = {:?}", b);
println!("c after = {:?}", c);
}
注意可能引发的内存泄露,可考虑使用Weak智能指针避免。
无畏并发
创建子线程 spawn和move关键字
use std::thread;
use std::time::Duration;
// 创建子线程
let handle = thread::spawn(|| {
for i in 1..10 {
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 会阻塞当前线程直到 handle 所代表的线程结束。
handle.join().unwrap();
// move 关键字,移交当前环境的所有权给闭包
let v = vec![12, 34, 56];
let handle_2 = thread::spawn(move || {
println!("Here's a vector: {:?}", v);
});
handle_2.join().unwrap();
mpsc 多个生产者单个消费者通道 (multiple producer, single consumer)
单次传值
// 使用消息传递在线程间传送数据
// 编程中的通道有两部分组成,一个发送者(transmitter)和一个接收者(receiver)
let (tx, rx) = mpsc::channel(); // mpsc 是 多个生产者,单个消费者(multiple producer, single consumer)的缩写。
thread::spawn(move || {
let val = String::from("hi");
tx.send(val).unwrap();
});
// 通道的接收端有两个有用的方法:recv 和 try_recv。如下两部分代码只能选其一运行
// 这里,我们使用了 recv,它是 receive 的缩写。这个方法会阻塞主线程执行直到从通道中接收一个值。
let received = rx.recv().unwrap();
println!("Got: {}", received);
// try_recv 不会阻塞,相反它立刻返回一个 Result<T, E>:Ok 值包含可用的信息,而 Err 值代表此时没有任何消息。如果线程在等待消息过程中还有其他工作时使用 try_recv 很有用:可以编写一个循环来频繁调用 try_recv,在有可用消息时进行处理,其余时候则处理一会其他工作直到再次检查。
loop {
if let Ok(v) = rx.try_recv() {
println!("Got: {} by try_recv", v);
break;
}
}
一传多值
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let vals = vec![
String::from("hi"),
String::from("from"),
String::from("the"),
String::from("thread"),
];
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
for received in rx {
println!("Got: {}", received);
}
多传多值
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();// 通过clone,创建多个producer
thread::spawn(move || {
let vals = vec![
String::from("hi"),
String::from("from"),
String::from("the"),
String::from("thread"),
];
for val in vals {
tx1.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
thread::spawn(move || {
let vals = vec![
String::from("more"),
String::from("messages"),
String::from("for"),
String::from("you"),
];
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
for received in rx {
println!("Got: {}", received);
}
Mutex 互斥锁 和 Arc 原子计数器
// 单线程下演示
let m = Mutex::new(5);
{
// 锁上
let mut num = m.lock().unwrap();
*num = 6;
} // 出作用域之后自动解锁
println!("{:?}", m);
// 多线程共享状态
// Arc 原子计数器。Arc 是 Rc 的线程安全替代
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());
// RefCell/Rc 和 Mutex/Arc 有相似性
模式匹配语法
match 分支
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
_ => EXPRESSION, // 通配
}
// 匹配守卫
let num = Some(4);
match num {
Some(x) if x < 5 => println!("less than five: {}", x),
Some(x) => println!("{}", x),
None => (),
}
if let 表达式
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
if let Some(color) = favorite_color {
println!("Using your favorite color, {}, as the background", color);
} else if is_tuesday {
println!("Tuesday is green day!");
} else if let Ok(age) = age {
if age > 30 {
println!("Using purple as the background color");
} else {
println!("Using orange as the background color");
}
} else {
println!("Using blue as the background color");
}
}
// 输出 Using purple as the background color
while let
#![allow(unused)]
fn main() {
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
// 一直pop,直到变成None
while let Some(top) = stack.pop() {
println!("{}", top);
}
}
变量解构
// let 解构
let (x, y, z) = (1, 2, 3);
let [r, g, b] = [233, 244, 255];
// 函数参数解构
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("location: {} {}", x, y);
}
// 结构体解构
struct Point {
x: i32,
y: i32,
}
let p = Point { x: 0, y: 7 };
let Point{ x, y } = p;
// 给x,y取别名
let Point{ x:a, y:b } = p;
// 忽略一些值
let Point{ x, ..} = p;
高级类型
type 关键字(别名)
// Thunk 是后面三个类型的同义词,从而避免重复代码
type Thunk = Box<dyn Fn() + Send + 'static>;
let f: Thunk = Box::new(|| println!("hi"));
fn takes_long_type(f: Thunk) {
// --snip--
}
fn returns_long_type() -> Thunk {
// --snip--
Box::new(|| ())
}