1.rust所有权
语句和表达式:前者不返回值,而后者返回值。在rust中,每一个代码块的最后一行可以放一个表达式,这个表达式就作为这整个代码块的返回值。
fn if_test() {
let condition = true;
let x = {
if condition {
12
} else {
let y = 99;
y + 1
}
};
println!("{}", x);
}
不同语言的垃圾清理规则:
- java:有gc收集器,当察觉到某个值没有使用之后,自动回收内存
- c++:开发者手动回收内存
- rust:当拥有这个值的变量走出作用范围后,内存就会自动收集(调用drop函数)
所有权,就是因为heap内存管理而产生的。rust中的所有权规则:
- 每个值都对应一个变量,这个变量就是这个值的所有者(变量拥有这个值)
- 每个值同时只能有一个所有者
- 当所有者超出作用域的时候,这个值就会被清理
fn test1() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}", s1); // 报错,因为"hello"的所有权被s2夺走,s1失效!
}
fn take_ownership(some_string: String) {
println!("{}", some_string);
}
fn ownership_test() {
/* 在这里例子中,s传入take_ownership中,该函数的some_string也指向了这个s指向的值
而String实现了drop strait,因此s从此失效。
这种情况,在rust中,我们说s“移动”到了some_string中
*/
let s = String::from("hello");
take_ownership(s);
// s已经失效了!报错
println!("{}", s);
}
上面的例子中,外部的String作为参数传进来后自己就失效了,这在有的情况下是不太方便的。因此,我们使用引用。注意下面的代码:
fn calculate_length(s: &String) -> usize {
// &表示引用:允许你引用某些值而!!!不取得他的所有权!!!!
// 我们把引用作为函数参数的行为叫做借用
// 引用不能被修改,比如这里就不能使用: s.push_str("hello")
s.len()
}
fn test() {
let s = String::from("hello");
let l = calculate_length(&s);
println!("{}", s); // 仍然有效!
}
引用不能被修改,那么可以不可以通过添加mut来让他可以被修改呢?注意下面的代码:
fn calculate_length_mut(s: &mut String) -> usize {
s.push_str("hello"); // 可以修改!
s.len()
}
fn mut_ref_test() {
let mut s = String::from("hello"); // 被引用的变量一定要是mut的
calculate_length_mut(&mut s); // 同一个作用域里不能同时存在多个可变引用,但是可以存在多个不可变引用
}
字符串切片:字符串切片(&str)其实就是字符串的部分引用,是不可变的:
fn string_stripe_test() {
let s = String::from("hello world");
let hello = &s[..5];
let world = &s[6..];
let whole = &s[..];
println!("{} {} is equal to: {}", hello, world, whole);
}
/*
这里有一个小建议:在定义函数的时候,完全使用字符串切片代替字符串引用,只有好处没有坏处,如下:
fn some_function(s: &String),不好
fn some_function(s: &str),好!
为什么好呢?因为你如果传进去的是字符串切片(比如let s = "hello"的s),直接传进去就好了
如果传进去的是String,那么就可以创建一个完整的字符串切片传进去,没有损失任何东西
比如 let my_string = String::from("hello")。这样子传进去:&my_string[..]
*/
2.结构体和枚举
结构体:
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn build_user(username: String, active: bool) -> User {
User {
email: String::from("aaa@email.com"),
// 如果传进来的参数名刚好和字段名相同,就可以简写
username,
sign_in_count: 111,
// 这里也是简写
active,
}
}
// 如果想要基于某一个struct实例来创建一个新的实例,可以使用struct的更新语法,如下
fn struct_update_test() {
let user1 = User {
email: String::from("aa@email.com"),
username: String::from("chr"),
active: true,
sign_in_count: 100,
};
let user2 = User {
username: String::from("zyj"),
// 这里直接更具user1来创建了user2,只是username不一样
..user1
};
println!("{},{},{},{}", user2.username, user2.email, user2.active, user2.sign_in_count);
}
fn tuple_struct_test() {
// 这个叫做tuple struct。适用于想要给一个tuple取名字,让他不同于其他的tuple
//(就算里边的每个元素的类型都是一样的。就像下边的Color和Point一样)
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}
struct Rectangle {
width: u32,
length: u32,
}
// 为这个struct定义一个方法
impl Rectangle {
fn area(&self) -> u32 {
// 里边使用&,表明我们这里不需要它的所有权(当然也可以去掉&,甚至是加上mut)
// 在调用的时候,不需要使用(&rec).area()
// 因为rust他会自动根据情况来添加&,&mut,或者是*
self.length * self.width
}
// 关联函数(他不是一个方法!)(其实就是java中的静态函数,dddd)
// 通常用于构造器
// 函数里边没有self,说明就是一个关联函数
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
length: size,
}
}
}
枚举类型:
enum Message {
// 枚举类型,下面的Quit、Move称为该枚举类型的变体
// 这个变体是可以附加信息的,比如这里的Write就附加了一个String字符串
Quit,
Move { x: u32, y: u32 },
Write(String),
ChangeColor(u32, u32, u32),
}
// 当然也可以为枚举类型创建方法,一样的
impl Message {
fn function(&self) {
println!("do nothing");
}
}
fn test_1() {
let q = Message::Quit;
let m = Message::Move { x: 12, y: 245 };
let w = Message::Write(String::from("hello"));
let c = Message::ChangeColor(1, 2, 3);
w.function();
}
/*
在其他编程语言中,几乎都存在null这个东西。
而在rust中,不存在null。在标准库中提供了一个类似的概念
Option<T>枚举
enum Option<T> {
Some(T),
None,
}
这样做,可以让None引发的相关错误在编译时期就被发现
*/
枚举类型常常配合 match 使用:
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> i32 {
// match 是一个表达式,因此会返回一个值
// 注意match里边是 fat arrow( => )
// match的分支中还可以绑定枚举类型的附加值!!!
// 同时,这个match必须覆盖到所有的匹配。比如对于Option枚举来说,必须要有Some和None的分支
// 当然,可以使用“_”来表示通配符
let value = match coin {
Coin::Penny => {
println!("it is a penny!");
1
},
Coin::Nickel => {
println!("it is a Nickel!");
5
},
Coin::Dime => {
println!("it is a Dime!");
10
},
Coin::Quarter => {
println!("it is a Quarter!");
25
},
};
value
}
fn main() {
test_1();
let my_coin = Coin::Dime;
let my_value = value_in_cents(my_coin);
}
3.常用容器
fn test() {
let mut v = vec![1, 2, 3];
// 使用for i in v默认会调用v的into_iter()方法,会得到v的所有权
// 所以这里使用了可变引用(因为我们还想要改变里边的数字)
for i in &mut v {
*i += 100;
}
let number = v[0];
println!("{}", number);
}
use std::{collections::HashMap, vec};
fn test06() {
let mut hm = HashMap::new();
hm.insert(String::from("blue"), 10);
hm.insert(String::from("yellow"), 15);
// 另一种创建hashmap的方法:使用collect
// collect可以把数据整合成很多集合类型,包括HashMap
let teams = vec![String::from("blue"), String::from("yellow")];
let scores = vec![12, 11];
// 这里必须提前声明这个变量的类型。因为collect方法可以整合成许多类型
let zip_hashmap: HashMap<_, _> = teams.iter().zip(scores.iter()).collect();
}
fn test07() {
// HashMap和所有权
// 被添加进入hashmap的数据的所有权会被hashmap夺走
let mut h = HashMap::new();
let s1 = String::from("hello");
let s2 = String::from("world");
h.insert(s1, s2);
// println!("{}, {}", s1, s2); // 报错,因为s1和s2的所有权已经被夺走了
// h.insert(&s1, &s2) 这样所有权不会被夺走
}
fn test08() {
// 如何获取Hashmap的值呢?使用get方法,同样,返回一个Option
let mut h = HashMap::new();
h.insert(String::from("hello"), 11);
h.insert(String::from("world"), 22);
let s = String::from("wold");
// 注意get方法需要接收的是一个引用类型!
let score = h.get(&s);
match score {
Option::Some(number) => println!("{}", number),
Option::None => println!("none!"),
};
// 如何对hashmap遍历?注意&!!!!因为通常来说,我们遍历了hashmap之后还要使用它
// 你如果不加&,那么hashmap的所有权就被夺走了!反复强调!
for (k, v) in &h {
println!("{}: {}", k, v);
}
}
fn test09() {
// 如何修改hashmap中的值呢?
let mut scores = HashMap::new();
scores.insert(String::from("yellow"), 25);
// 检查blue这个key存不存在?返回一个Entry
let e = scores.entry(String::from("blue"));
// 这个or_insert方法会检查这个entry对象有没有value,如果没有,则插入一个默认值
// 最后返回这个value的可变引用
e.or_insert(50);
// 由于yellow已经存在了,所以就没有插入
scores.entry(String::from("yellow")).or_insert(100);
println!("{:?}", scores);
}
fn test10() {
// 这个or_insert方法,他会返回这个key所对应的value的可变引用。注意下面这个例子:
let text = "hello world fan print fan fan sit";
let mut compute = HashMap::new();
for word in text.split_whitespace() {
// 下面这个times的类型是 &mut i32,对i32的可变引用
let times = compute.entry(word).or_insert(0);
*times += 1;
}
println!("{:#?}", compute);
}
4.错误处理
在rust中的错误处理通常有两种:
- 可恢复的错误:返回一个Result<T, E>,将错误传递下去
- 不可恢复的错误:使用panic!宏中断
如下是打开文件的处理方式
fn test() {
let f = File::open("hello.txt");
let f = match f {
// 这里的file和error变量都是通过模式匹配结构出来的临时变量(枚举变体携带的信息)
Result::Ok(file) => file,
Result::Err(error) => {
panic!("error opening the ile: {:?}", error);
}
};
}
// 传播错误,而不去解决它。看下面一个例子
fn read_username_from_file() -> Result<String, io::Error> { // 这里模仿其他方法,将该函数的执行结果返回
let f = File::open("hello.txt");
let mut f = match f {
Result::Ok(file) => file,
Result::Err(e) => return Result::Err(e),
};
let mut s = String::new();
// 注意到,这里的read_to_string方法用到了自身的可变引用,因此上边的f必须是mut的
match f.read_to_string(&mut s) {
// 注意,这里我们没有用到Ok的这个附加值,因为我们要的是可变引用s
Result::Ok(_) => Result::Ok(s),
Result::Err(e) => Result::Err(e),
}
}
// 使用 ? 来修改上面的函数
fn read_username_from_file_improved() -> Result<String, io::Error> {
/*
把?作用于一个Result,则有两种可能:
1. 如果Result是Ok,则Ok附带的值就是整个表达式的值,程序继续;
2. 如果Result是Err,则Err就是整个函数!!!的返回值(相当于直接使用了return Err
*/
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Result::Ok(s)
// ? 只能用于返回值为Result的函数里边!
}
5.trait
rust中的trait和java中的interface差不多(个人感觉)
// 定义一个Summary trait,实现这个trait的结构体必须实现trait里边的方法
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
将trait作为函数参数:
pub fn notify(item: impl Summary) {
println!("this item has implemented the trait: {}", item.summarize());
}
// 使用加号来表明这个类型需要实现多个trait
pub fn notify_1<T: Summary + Display>(item: T) {
println!("this is more simple, {}", item.summarize());
}
// 但是多个参数都指定多个trait,这会导致函数签名不好看
pub fn notify_2<T, U>(a: T, b: U) -> String
where
T: Summary + Display,
U: Clone + Debug,
{
format!("this way: {}", a.summarize())
}
6.生命周期
生命周期的主要目标就是:避免悬垂引用
当函数参数或结构体包含多个引用时,Rust 编译器需要明确知道这些引用的存活关系,否则会报错要求手动标注
生命周期的标注:描述了多个引用的生命周期之间的关系(因此标注单个引用的生命周期没有任何意义),但是不会影响到生命周期
下面是一个典型的场景(函数的返回引用依赖于输入参数)
// 错误版本
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
// 正确标注版本
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
...
}
当结构体中包含引用时,同样,结构体不能存活的比引用长:
struct BookShelf<'a> {
books: &'a [String],
}
fn main() {
let my_books = vec![
String::from("rust programming"),
String::from("cpp programming")
];
let shelf = BookShelf {
books: &my_books
};
}
生命周期的省略规则:
- 每个引用类型的输入参数都有自己的生命周期
- 如果只有一个输入生命周期桉树,那么该生命周期被赋给所有输出生命周期参数
- 如果有多个输入生命周期参数,但是其中一个是 &self 或者 &mut self,则 self 的生命周期会被赋给所有输出生命周期参数
经过上面的规则后,如果仍然存在没有确定生命周期的引用,那么编译器就会报错
7.函数式编程
函数式编程的风格: 将一个函数作为参数传入函数中; 将函数作为返回值进行返回; 将函数赋值给变量,晚点执行。。。
闭包: 是匿名函数 保存为变量、作为参数 可以在一个地方创建闭包,然后在另一个上下文调用闭包;来完成运算 可从其定义的作用域捕获值
fn generate_workout(intensity: u32, random_number: u32) {
// 这就是一个闭包,将一段函数赋值给一个变量
// 闭包不需要标注参数类型,以及返回值类型
let mut expensive_closure = Cacher::new(| num: u32 | {
println!("calculating slowly....");
thread::sleep(Duration::from_secs(3));
num
});
if intensity < 25 {
println!("Today, do {} pushups!", expensive_closure.value(intensity));
println!("Next, do {} situps!", expensive_closure.value(intensity));
} else {
if random_number == 3 {
println!("take a break.");
} else {
println!("run for {} minutes!", expensive_closure.value(intensity));
}
}
}
// 使用struct来存储闭包,可以达到如下效果:
// 当闭包需要被执行的时候,就执行,同时结构体可以将这个执行的值给保存下来
// 这样,无论使用多少次闭包的值,闭包都只会被执行一次!
struct Cacher<T>
where
// 当需要指定fn的trait时,首先使用fn。如果闭包里边的情况会需要实现FnOnce或者FnMut,编译器会告诉你
T: Fn(u32) -> u32,
{
calculate: T,
value: Option<u32>,
}
impl<T> Cacher<T>
where
T: Fn(u32) -> u32
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation: calculation,
value: Option::None
}
}
fn value(&mut self, args: u32) -> u32 {
match self.value {
Option::Some(val) => val,
Option::None => {
let val = (self.calculation)(args);
self.value = Option::Some(val);
val
}
}
}
}
迭代器相关:
- iter():创建不可变引用迭代器
- into_iter():取得容器所有权(使用for e in v默认调用into_iter())
- into_iter():创建可变引用迭代器
例子:
fn iter_test() {
let mut v = vec![String::from("hello"), String::from("hey")];
let mut it1 = v.iter();
let e1 = it1.next(); // e1的类型是Option<&String>
// iter_mut方法,得到 &mut String
let mut it3 = v.iter_mut();
let e3 = it3.next();
// 使用into_iter方法,创建的迭代器会取得所有权 String
let mut it2 = v.into_iter();
let e2 = it2.next(); // Option<String>
}
// map方法,collect方法常用
fn map_test() {
let v1 = vec![1, 2, 3];
let it1 = v1.iter();
// 在iter上调用map方法会产生一个新的iter
let it2 = it1.map(|val| {
val + 1
});
// 在iter上调用collect方法会将所有元素放到一个集合里边,collect方法是一个消耗性方法
let v2: Vec<_> = it2.collect();
for i in v2 {
println!("{}", i);
}
// 原来的集合没有变化
for i in v1 {
println!("{}", i);
}
// 通常像下面一样使用链式法则
// let v3: Vec<_> = v1.iter().map(|a| a + 1).collect();
}
迭代器的 filter、map、collect、next都是很常用的
struct Shoe {
size: u32,
style: String,
}
fn shoes_in_my_size(shoes: Vec[Shoe], my_size: u32) -> Vec<Shoe> {
shoes.into_iter().filter(| shoe | {
shoe.size == my_size
}).collect()
}
8.智能指针
/**
* Box<T>是最简单的智能指针,它在stack上拥有一个指针,其指向heap上的数据
* 没有其他的额外功能了
* 那我感觉Box<T>其实就是Cpp中的指向T类型的指针
* 所以以后想要在rust中使用指针,直接使用Box就行
*/
fn box_test_1() {
// 这里的10就是放在heap上的
let a = Box::new(10);
println!("a is: {}", a);
// 当a离开作用域时,它会释放这个10以及在stack上的指针
}
// 可以使用Box来实现rust中的链表:
enum List {
// Cons中有一个指向List的指针
Cons(i32, Box<List>),
Nil,
}
// 用来遍历一个链表!
fn read_a_list(l: &List) {
let result = match l {
// 这里的next是 &Box<List>
List::Cons(value, next) => {
println!("{}", value);
read_a_list(next); // 由于Box实现了Deref strait,他会自动隐式解引用 &Box<List> -> &List
},
List::Nil => {},
};
}
deref trait:
fn deref_test_1() {
let x = 14;
let y = &x; // 这里y是一个指针(引用)
assert_eq!(x, 14);
assert_eq!(*y, 14); // 这里需要解引用才能获得y所指向的值
}
fn deref_test_2() {
let x = 14;
let y = Box::new(x);
assert_eq!(x, 14);
assert_eq!(*y, 14); // Box实现了deref,因此我们可以使用*y来访问它所指向的值
}
/**
* 函数和方法的隐式解引用转化
* 当把某类型的引用传递给函数或者方法时,但他的类型与定义的参数类型不匹配,
* 编译器就会自动调用deref转换
* 如下一个例子(在编译的时候发生,因此没有额外的性能开销):
*/
fn hello(name: &str) {
println!("hello, {}", name);
}
fn deref_test_4() {
let m = MyBox::new(String::from("rust"));
// 这里 &MyBox<String> --> &String --> &str
// 编译器自动不断地调用 deref方法,从而最终转化为 &str
// 不然就应该是这样的: hello(&(*m)[..]),其中 *m 是String
hello(&m);
}
Rc指针:
fn rc_test_1() {
/**
* 这里的要求是,a是一个链表,5--10--nil
* 我们想要创建b链表: 3--a(5--10--nil),且不复制
* c链表:4--a(5--10-nil)
* 如果还要使用普通的Box来完成,就会发现,a如果被b链接了,它的所有权就被b拿走了
* 再想要使用c链接a就不行了
*
* 这种需要多个指针指向一个数据的情况,就需要使用Rc<T>这个指针!具体的使用如下:
*/
let a = Rc::new(ListRc::Cons(5, Rc::new(ListRc::Cons(10, Rc::new(ListRc::Nil)))));
println!("count after creating a = {}", Rc::strong_count(&a));
// Rc::clone() 它只是增加引用,不会执行数据的深度拷贝操作
// 相当于b多了一个3节点,然后连接上a
let b = ListRc::Cons(3, Rc::clone(&a));
println!("count after creating b = {}", Rc::strong_count(&a));
{
let c = ListRc::Cons(4, Rc::clone(&a));
println!("count after creating c = {}", Rc::strong_count(&a));
}
println!("count after c dies = {}", Rc::strong_count(&a));
}
fn my_rc_test() {
// 以下示例中,s, pt1, pt2同时指向字符串hello
let s = Rc::new(String::from("hello"));
println!("{}", Rc::strong_count(&s));
// let pt1 = Rc::new(s);
// let pt2 = Rc::new(s); 报错,因为s的所有权已经被pt1抢走了!
let pt1 = Rc::clone(&s);
println!("{}", Rc::strong_count(&s));
let pt2 = Rc::clone(&s);
println!("{}", Rc::strong_count(&s));
}
RefCell智能指针,可以:
- 通过不可变引用修改内部数据
- 在运行时借用检查,如果违反规则就出发panic
- 单线程专用
fn refCell_test() {
let r = RefCell::new(String::from("hello"));
{
// 通过不可变引用修改内部数据
let mut change = r.borrow_mut();
change.push_str(", rust!");
}
// 输出 hello, rust!
println!("{}", r.borrow());
}
// 一个常用的场景是,搭配Rc指针,实现多个所有者共享可变数据
fn test1() {
let account = Rc::new(RefCell::new(100));
let chr = Rc::clone(&account);
let zyj = Rc::clone(&account);
*chr.borrow_mut() += 100;
*zyj.borrow_mut() -= 10;
// 输出190
println!("there are {} left in the account.", account.borrow());
}
RefCell 要求在任何时刻,只能存在:
- 一个可变借用(borrow_mut)
- 或者多个不可变借用(borrow)
9.并发
/**
* Concurrent(并发): 程序不同部分独立执行
* Parallel(并行): 程序不同部分同时运行
*
* 实现线程的方式:
* 1. 调用OS的api创建线程,1:1(运行时较小,一个操作系统的线程对应一个语言中创建的线程)Rust
* 2. 语言自己实现的线程,M:N(运行时更大)
*/
use std::thread::{self, spawn};
use std::time::Duration;
fn test_1() {
// main程序结束后,不管这个spawned线程有没有结束,整个程序结束
// 因此这个线程大概率是执行不完的
thread::spawn(|| {
for i in 1..10 {
println!("number {} from spawned thread.", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("number {} from main thread.", i);
thread::sleep(Duration::from_millis(1));
}
}
fn test_2() {
let handle = thread::spawn(|| {
for i in 1..10 {
println!("number {} from spawned thread.", i);
thread::sleep(Duration::from_millis(1));
}
});
// 调用handle的join方法会阻塞当前运行线程的执行,直到handle所表示的线程终结
// 因此这里就会spawned线程1到10,再执行主线程的1到5
handle.join().unwrap();
for i in 1..5 {
println!("number {} from main thread.", i);
thread::sleep(Duration::from_millis(1));
}
}
fn test3() {
let v = vec![1, 2, 3];
// 这里我在线程中使用到了闭包外部的数据 v,提示有误
// 因为v的寿命有可能比这个闭包还要短,这会导致问题
// 所以现在可以使用move关键字来使得闭包获得 v 的所有权
let handle = thread::spawn(move || {
for e in v.iter() {
println!("{}", e);
}
});
handle.join().expect("something wrong.");
}
消息传递:
use std::sync::mpsc;
fn test_4() {
// 消息传递
// 使用mpsc::channel创建一个通道,multiple producer and single consumer
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
// 这个线程必须拥有通道发送端的所有权才能往通道里发送消息
let value = String::from("hello!");
sender.send(value).unwrap();
});
// 这里的recv方法会阻塞当前的线程,直到有消息传入到通道
// try_recv方法不会阻塞
let received= receiver.recv().unwrap();
println!("Got: {}", received);
}
fn test_5() {
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
let values = vec![String::from("Cpp"),
String::from("java"),
String::from("rust"),
String::from("C#")];
for value in values.into_iter() {
// 这里不能使用iter(),因为我们必须获得它的所有权才能send
sender.send(value).unwrap();
thread::sleep(Duration::from_millis(500));
}
});
// 更推荐使用这种方法来接收消息,底层用到了recv方法
// 会阻塞!
for received in receiver {
println!("Got: {}", received);
}
}
// 使用多个发送者
fn test_6() {
let (sender, receiver) = mpsc::channel();
// 克隆!
let another_sender = mpsc::Sender::clone(&sender);
thread::spawn(move || {
let values = vec![String::from("Cpp"),
String::from("java"),
String::from("rust"),
String::from("C#")];
for value in values.into_iter() {
sender.send(value).unwrap();
thread::sleep(Duration::from_millis(500));
}
});
thread::spawn(move || {
let values = vec![String::from("###Cpp"),
String::from("###java"),
String::from("###rust"),
String::from("###C#")];
for value in values.into_iter() {
another_sender.send(value).unwrap();
thread::sleep(Duration::from_millis(500));
}
});
// 更推荐使用这种方法来接收消息,底层用到了recv方法
// 会阻塞!
for received in receiver {
println!("Got: {}", received);
}
}
// 以上都是使用通信的方法实现并发
use std::sync::Mutex;
fn test_7() {
// Mutex 是一个智能指针,互斥锁
let m = Mutex::new(5);
{
// 访问数据前,先使用lock来获得锁。这个方法是阻塞的
// lock可能失败,返回一个MetexGuard智能指针
let mut num = m.lock().unwrap();
*num += 12;
}
println!("{}", m.lock().unwrap());
}
use std::sync::Arc;
// 使用Arc来进行原子引用计数(和Rc一模一样,只是用在多线程环境下)
fn test_8() {
// let counter = Mutex::new(0);
let counter = Arc::new(Mutex::new(0));
let mut handles = Vec::new();
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.into_iter() {
handle.join().unwrap();
}
println!("{}", counter.lock().unwrap());
}