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());
}