- what does “at the same time” means”
- in the same scope
- Common code patterns in rust because of the borrowing rules
- new scopes
- To indicate where a borrow should end
- temporary variables
- to end a borrow and get a result before another borrow
- entry API
- for updating or inserting into a data struct
- splitting up struts
- methods only borrow fields they use
- new scopes
Borrow Scope
fn main() {let list = vec![1,2,3];let list_first = list.first();let list_last = list.last();println!("first {:?} last {:?}", list_first, list_last);}
not compile
fn main() {let mut list = vec![1, 2, 3];let first_mut = list.first_mut().expect("list was empty");*first_mut = 3;let list_first = list.first();let list_last = list.last();println!("first {:?} last {:?} first_mut {:?}", list_first, list_last, first_mut);}
error[E0502]: cannot borrow `list` as immutable because it is also borrowed as mutable--> src/main.rs:7:22|4 | let first_mut = list.first_mut().expect("list was empty");| ---- mutable borrow occurs here...7 | let list_first = list.first();| ^^^^ immutable borrow occurs here...11 | println!("first {:?} last {:?} first_mut {:?}", list_first, list_last, first_mut);| --------- mutable borrow later used here
这段代码无法编译通过,原因是违反了borrowcheck的规则,变量list不能同时被 mut borrow以及immutable borrow
Mut Borrow
fn main() {let mut list = vec![1, 2, 3];let first_mut = list.first_mut().expect("list was empty");*first_mut += 3;let list_first = list.first();let list_last = list.last();println!("first {:?} last {:?}", list_first, list_last);}
但是这段代码可以编译通过,原因是编译器发现 first_mut 在使用后没有再被引用,其生命周期没有和immutable borrow重合
fn main() {let mut list = vec![1, 2, 3];let list_first = list.first();let list_last = list.last();println!("first {:?} last {:?}", list_first, list_last);let first_mut = list.first_mut().expect("list was empty");*first_mut = 3;}
同理,当 first_mut 在 list_frist 以及 list_last 之后时,虽然此时 immutable与mutable的scope有重合,但compiler发现 list_first与list_last 在println之后就没有再被引用了,所以也没有break rule
temp variable
pub struct Player {score: i32}impl Player {pub fn set_score(&mut self, new_score: i32) {self.score = new_score;}pub fn score(&self) -> i32 {self.score}pub fn new() -> Self {Player{score: 0}}}fn main() {let mut player1 = temporary_variable::Player::new();player1.set_score(player1.score() + 1);println!("score {:?}", player1.score());}
在旧版本的compiler中,这段代码是编译不过的,原因是 player1.set_score(player1.score() + 1); 这段代码同时mutable以及immutable borrow player1,但是在新版本的compiler中,这段代码是合法的,编译器做了优化
Entry API
fn counting_words() {let text = "hello world hello";let mut freqs = HashMap::<&str, i32>::new();for word in text.split_whitespace() {match freqs.get_mut(word) {Some(value) => *value += 1,None => {freqs.insert(word, 1);}}}println!("word frequences: {:#?}", freqs);}
这段代码在旧版本的编译器中也是编译不过的,编译器任务freqs.get_mut & freqs.insert 在一个scope内同时mutable borrow了freqs,在新版本的编译器中这段代码也是合法的,但是这段代码有更好的写法
fn counting_words_with_entry_api() {let text = "hello world hello";let mut freqs = HashMap::<&str, i32>::new();for word in text.split_whitespace() {*freqs.entry(word).or_insert(0) += 1;}println!("word frequences: {:#?}", freqs);}
这里 entry 会返回一个enum,这个enum有一个or_insert方法,也就是说当不存在的时候insert 0, 同时返回一个&mut value, 这样就很优雅了
Split struct
pub struct Monster {hp: u8,sp: u8,frients: Vec<Friend>,}pub struct Friend {loyalty: u8,}impl Friend {pub fn new() -> Self {Friend {loyalty: 0}}}impl Monster {pub fn final_breath(&mut self) {if let Some(firend) = self.frients.first() {self.hp += firend.loyalty;self.sp -= firend.loyalty;println!("healing for {}", firend.loyalty);}}pub fn new() -> Self {Monster {hp: 100,sp: 100,frients: vec![Friend::new()]}}}fn main() {let mut m = split_struct::Monster::new();m.final_breath();}
这段代码没有问题, 但当我们对它进行重构,抽取当中一段代码的时候出现了问题
pub fn final_breath(&mut self) {if let Some(firend) = self.frients.first() {self.heal(firend.loyalty);println!("healing for {}", firend.loyalty);}}pub fn heal(&mut self, amount: u8) {self.hp += amount;self.sp -= amount;}
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable--> src/split_struct.rs:22:7|21 | if let Some(firend) = self.frients.first() {| ------------ immutable borrow occurs here22 | self.heal(firend.loyalty);| ^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here23 | println!("healing for {}", firend.loyalty);| -------------- immutable borrow later used here
因为heal mutable 引用了self,但是compiler不知道 heal函数没有用到friends这个字段,所以这里编译不过了,解决方法就是把 hp sp再抽象一个struct出来,将heal方法挂到这个函数上
pub struct Stats {hp: u8,sp: u8,}pub struct Monster {stats: Stats,frients: Vec<Friend>,}impl Monster {pub fn final_breath(&mut self) {if let Some(firend) = self.frients.first() {self.stats.heal(firend.loyalty);println!("healing for {}", firend.loyalty);}}pub fn new() -> Self {Monster {stats: Stats{hp: 100,sp: 100,},frients: vec![Friend::new()]}}}impl Stats {pub fn heal(&mut self, amount: u8) {self.hp += amount;self.sp -= amount;}}
Non-Lexical Lifetimes (NLL)
- Borrows that aren’t used through the end of the scope
- Borrows only used for part of an expresson
- Borrows that aren’t used in an arm
