- 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 here
22 | self.heal(firend.loyalty);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
23 | 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