• 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

Borrow Scope

  1. fn main() {
  2. let list = vec![1,2,3];
  3. let list_first = list.first();
  4. let list_last = list.last();
  5. println!("first {:?} last {:?}", list_first, list_last);
  6. }

not compile

  1. fn main() {
  2. let mut list = vec![1, 2, 3];
  3. let first_mut = list.first_mut().expect("list was empty");
  4. *first_mut = 3;
  5. let list_first = list.first();
  6. let list_last = list.last();
  7. println!("first {:?} last {:?} first_mut {:?}", list_first, list_last, first_mut);
  8. }
  1. error[E0502]: cannot borrow `list` as immutable because it is also borrowed as mutable
  2. --> src/main.rs:7:22
  3. |
  4. 4 | let first_mut = list.first_mut().expect("list was empty");
  5. | ---- mutable borrow occurs here
  6. ...
  7. 7 | let list_first = list.first();
  8. | ^^^^ immutable borrow occurs here
  9. ...
  10. 11 | println!("first {:?} last {:?} first_mut {:?}", list_first, list_last, first_mut);
  11. | --------- mutable borrow later used here

这段代码无法编译通过,原因是违反了borrowcheck的规则,变量list不能同时被 mut borrow以及immutable borrow

Mut Borrow

  1. fn main() {
  2. let mut list = vec![1, 2, 3];
  3. let first_mut = list.first_mut().expect("list was empty");
  4. *first_mut += 3;
  5. let list_first = list.first();
  6. let list_last = list.last();
  7. println!("first {:?} last {:?}", list_first, list_last);
  8. }

但是这段代码可以编译通过,原因是编译器发现 first_mut 在使用后没有再被引用,其生命周期没有和immutable borrow重合

  1. fn main() {
  2. let mut list = vec![1, 2, 3];
  3. let list_first = list.first();
  4. let list_last = list.last();
  5. println!("first {:?} last {:?}", list_first, list_last);
  6. let first_mut = list.first_mut().expect("list was empty");
  7. *first_mut = 3;
  8. }

同理,当 first_mut 在 list_frist 以及 list_last 之后时,虽然此时 immutable与mutable的scope有重合,但compiler发现 list_first与list_last 在println之后就没有再被引用了,所以也没有break rule

temp variable

  1. pub struct Player {
  2. score: i32
  3. }
  4. impl Player {
  5. pub fn set_score(&mut self, new_score: i32) {
  6. self.score = new_score;
  7. }
  8. pub fn score(&self) -> i32 {
  9. self.score
  10. }
  11. pub fn new() -> Self {
  12. Player{
  13. score: 0
  14. }
  15. }
  16. }
  17. fn main() {
  18. let mut player1 = temporary_variable::Player::new();
  19. player1.set_score(player1.score() + 1);
  20. println!("score {:?}", player1.score());
  21. }

在旧版本的compiler中,这段代码是编译不过的,原因是 player1.set_score(player1.score() + 1); 这段代码同时mutable以及immutable borrow player1,但是在新版本的compiler中,这段代码是合法的,编译器做了优化

Entry API

  1. fn counting_words() {
  2. let text = "hello world hello";
  3. let mut freqs = HashMap::<&str, i32>::new();
  4. for word in text.split_whitespace() {
  5. match freqs.get_mut(word) {
  6. Some(value) => *value += 1,
  7. None => {
  8. freqs.insert(word, 1);
  9. }
  10. }
  11. }
  12. println!("word frequences: {:#?}", freqs);
  13. }

这段代码在旧版本的编译器中也是编译不过的,编译器任务freqs.get_mut & freqs.insert 在一个scope内同时mutable borrow了freqs,在新版本的编译器中这段代码也是合法的,但是这段代码有更好的写法

  1. fn counting_words_with_entry_api() {
  2. let text = "hello world hello";
  3. let mut freqs = HashMap::<&str, i32>::new();
  4. for word in text.split_whitespace() {
  5. *freqs.entry(word).or_insert(0) += 1;
  6. }
  7. println!("word frequences: {:#?}", freqs);
  8. }

这里 entry 会返回一个enum,这个enum有一个or_insert方法,也就是说当不存在的时候insert 0, 同时返回一个&mut value, 这样就很优雅了

Split struct

  1. pub struct Monster {
  2. hp: u8,
  3. sp: u8,
  4. frients: Vec<Friend>,
  5. }
  6. pub struct Friend {
  7. loyalty: u8,
  8. }
  9. impl Friend {
  10. pub fn new() -> Self {
  11. Friend {
  12. loyalty: 0
  13. }
  14. }
  15. }
  16. impl Monster {
  17. pub fn final_breath(&mut self) {
  18. if let Some(firend) = self.frients.first() {
  19. self.hp += firend.loyalty;
  20. self.sp -= firend.loyalty;
  21. println!("healing for {}", firend.loyalty);
  22. }
  23. }
  24. pub fn new() -> Self {
  25. Monster {
  26. hp: 100,
  27. sp: 100,
  28. frients: vec![Friend::new()]
  29. }
  30. }
  31. }
  32. fn main() {
  33. let mut m = split_struct::Monster::new();
  34. m.final_breath();
  35. }

这段代码没有问题, 但当我们对它进行重构,抽取当中一段代码的时候出现了问题

  1. pub fn final_breath(&mut self) {
  2. if let Some(firend) = self.frients.first() {
  3. self.heal(firend.loyalty);
  4. println!("healing for {}", firend.loyalty);
  5. }
  6. }
  7. pub fn heal(&mut self, amount: u8) {
  8. self.hp += amount;
  9. self.sp -= amount;
  10. }
  1. error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  2. --> src/split_struct.rs:22:7
  3. |
  4. 21 | if let Some(firend) = self.frients.first() {
  5. | ------------ immutable borrow occurs here
  6. 22 | self.heal(firend.loyalty);
  7. | ^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
  8. 23 | println!("healing for {}", firend.loyalty);
  9. | -------------- immutable borrow later used here

因为heal mutable 引用了self,但是compiler不知道 heal函数没有用到friends这个字段,所以这里编译不过了,解决方法就是把 hp sp再抽象一个struct出来,将heal方法挂到这个函数上

  1. pub struct Stats {
  2. hp: u8,
  3. sp: u8,
  4. }
  5. pub struct Monster {
  6. stats: Stats,
  7. frients: Vec<Friend>,
  8. }
  9. impl Monster {
  10. pub fn final_breath(&mut self) {
  11. if let Some(firend) = self.frients.first() {
  12. self.stats.heal(firend.loyalty);
  13. println!("healing for {}", firend.loyalty);
  14. }
  15. }
  16. pub fn new() -> Self {
  17. Monster {
  18. stats: Stats{
  19. hp: 100,
  20. sp: 100,
  21. },
  22. frients: vec![Friend::new()]
  23. }
  24. }
  25. }
  26. impl Stats {
  27. pub fn heal(&mut self, amount: u8) {
  28. self.hp += amount;
  29. self.sp -= amount;
  30. }
  31. }

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