1 00:00:01,680 --> 00:00:05,850 There are a number of code patterns that you'll see in Rust that are useful because of the borrowing rules. 2 00:00:06,440 --> 00:00:11,490 We'll start off by going into more detail about what we mean by borrows happening "at the same time." 3 00:00:11,980 --> 00:00:15,240 We mentioned this concept in the previous module on borrowing and mutability, 4 00:00:15,540 --> 00:00:17,850 but we didn't go into detail about what that looks like. 5 00:00:18,070 --> 00:00:18,950 We'll do so here. 6 00:00:20,100 --> 00:00:22,770 Then, we'll take a look at some situations you might find yourself in, 7 00:00:23,010 --> 00:00:25,290 and the code patterns to use in those situations. 8 00:00:26,010 --> 00:00:28,310 The patterns are introducing new scopes, 9 00:00:28,620 --> 00:00:30,150 using temporary variables, 10 00:00:30,540 --> 00:00:31,900 using the Entry API, 11 00:00:32,090 --> 00:00:34,810 and splitting up a struct into multiple smaller structs. 12 00:00:35,740 --> 00:00:36,310 Finally, 13 00:00:36,320 --> 00:00:37,449 we'll touch on some improvements 14 00:00:37,450 --> 00:00:42,450 eventually coming to the Rust compiler that make the borrow checker match human intuition more closely. 15 00:00:43,940 --> 00:00:52,550 Let's get started with some more details on what "at the same time" means. In most cases, when we say borrows happen "at the same time," 16 00:00:52,940 --> 00:00:56,250 that means they're in the same lexical scope created by curly brackets. 17 00:00:57,740 --> 00:00:58,730 In this example, 18 00:00:58,740 --> 00:01:01,350 we have two immutable borrows "at the same time": 19 00:01:01,940 --> 00:01:07,350 the reference stored in list_first lasts from where it's declared until the closing curly brace of main. 20 00:01:08,180 --> 00:01:13,150 The reference in list_last is also valid from its declaration until the end of main. 21 00:01:14,720 --> 00:01:17,750 This code works fine because both references are immutable, 22 00:01:18,140 --> 00:01:20,650 which is allowed under the rules we covered in the previous module. 23 00:01:22,240 --> 00:01:22,890 However, 24 00:01:23,060 --> 00:01:30,710 if we make the list mutable and introduce a variable named list.first_mut that holds a mutable reference, 25 00:01:31,240 --> 00:01:37,950 this example will no longer compile. Because the immutable borrows last until the end of main scope, 26 00:01:38,340 --> 00:01:41,880 they happen at the same time as the mutable borrow we just introduced. 27 00:01:43,440 --> 00:01:46,450 Sometimes, "in the same scope" isn't quite accurate. 28 00:01:46,990 --> 00:01:48,969 Let's look at another example. Here, 29 00:01:48,970 --> 00:01:53,010 we're still calling the method first_mut that returns a mutable reference to list. 30 00:01:53,380 --> 00:01:55,960 But rather than storing the mutable reference in a variable, 31 00:01:56,290 --> 00:01:58,520 we're using it right away in one expression. 32 00:01:59,340 --> 00:02:02,710 Then, we take immutable references after we've mutated list. 33 00:02:03,240 --> 00:02:07,450 This example still has a mutable reference and immutable references in the same scope. 34 00:02:07,630 --> 00:02:09,209 So, this violates the borrowing rules, 35 00:02:09,210 --> 00:02:09,550 right? 36 00:02:10,940 --> 00:02:11,320 Nope! 37 00:02:11,600 --> 00:02:12,750 This code is actually allowed. 38 00:02:14,270 --> 00:02:20,779 The mutable borrow only lasts for the single expression because there's no variable holding on to the reference until the end of the function's 39 00:02:20,780 --> 00:02:23,470 scope. Since the mutable borrow ends right away 40 00:02:23,480 --> 00:02:25,250 and then the immutable borrows start, 41 00:02:25,640 --> 00:02:28,420 the immutable borrows aren't happening at the same time, 42 00:02:28,650 --> 00:02:30,650 even though they're happening in the same scope. 43 00:02:32,040 --> 00:02:33,550 Here's another variation. 44 00:02:34,340 --> 00:02:37,000 We're still using the mutable reference in a single expression, 45 00:02:37,310 --> 00:02:39,789 but this time we're mutating the list after we're 46 00:02:39,790 --> 00:02:43,770 done using the immutable references. Based on the last example, 47 00:02:43,980 --> 00:02:45,950 you might think that this example should work. 48 00:02:47,440 --> 00:02:52,550 As of Rust 1.24 - the version that we're using - this example doesn't work. 49 00:02:53,440 --> 00:03:01,030 The error shows that because the immutable borrow lasts until the end of main and the mutable borrow happens between the immutable borrow and the end of main, 50 00:03:01,220 --> 00:03:02,250 this isn't allowed. 51 00:03:04,240 --> 00:03:10,200 You might realize that this could be allowed because the immutable borrow isn't used between the mutable borrow and the end of main. 52 00:03:10,770 --> 00:03:13,850 The compiler is being overly conservative in its analysis. 53 00:03:14,340 --> 00:03:14,910 However, 54 00:03:15,340 --> 00:03:17,399 this code will work in the future, as we'll 55 00:03:17,400 --> 00:03:18,950 discuss at the end of this video. 56 00:03:20,440 --> 00:03:21,150 Next, 57 00:03:21,540 --> 00:03:24,970 let's talk about code patterns influenced by the borrowing rules, 58 00:03:25,280 --> 00:03:30,420 starting with adding new scopes. In the example we were just looking at, 59 00:03:30,570 --> 00:03:34,280 the problem was that the immutable borrows lasted until the end of main, 60 00:03:34,590 --> 00:03:41,409 even though we were finished using them before that point. 61 00:03:41,410 --> 00:03:42,690 To tell Rust that we're done with the immutable borrows before the end of main, 62 00:03:42,950 --> 00:03:44,750 we can add a new inner scope 63 00:03:45,140 --> 00:03:46,950 that ends before the outer scope does. 64 00:03:48,540 --> 00:03:49,480 In this example, 65 00:03:49,700 --> 00:03:50,879 the inner scope we're adding 66 00:03:50,880 --> 00:03:55,950 contains the declarations and usages of the variables containing the immutable borrows. 67 00:04:01,040 --> 00:04:01,700 This way, 68 00:04:01,890 --> 00:04:05,550 the immutable borrows will end at the closing curly bracket of the inner scope, 69 00:04:06,040 --> 00:04:11,350 and the compiler will no longer think that they happen at the same time as the mutable borrow in the last line of main. 70 00:04:13,070 --> 00:04:17,280 This code now compiles and runs. Next, 71 00:04:17,550 --> 00:04:19,589 let's look at an example where introducing 72 00:04:19,590 --> 00:04:22,650 a temporary variable is needed because of the borrowing rules. 73 00:04:24,340 --> 00:04:25,320 In this example, 74 00:04:25,330 --> 00:04:27,850 we've defined a Player struct that has a score field. 75 00:04:28,740 --> 00:04:30,720 Rather than making the score field public, 76 00:04:30,940 --> 00:04:33,650 we've implemented set_score and score methods. 77 00:04:34,340 --> 00:04:35,070 In main, 78 00:04:35,220 --> 00:04:40,239 we create a new mutable player, and then try to increment the score by setting it to its previous score 79 00:04:40,240 --> 00:04:41,050 plus one. 80 00:04:42,540 --> 00:04:43,980 This code doesn't compile; 81 00:04:44,300 --> 00:04:44,649 the error 82 00:04:44,650 --> 00:04:45,650 message says, 83 00:04:46,040 --> 00:04:50,550 cannot borrow `player1` asimmutable because it is also borrowed as mutable. 84 00:04:51,140 --> 00:04:56,410 The error message shows that the mutable borrow of player one starts with the call to set_score 85 00:04:56,530 --> 00:04:58,850 because that method borrows mut self. 86 00:04:59,340 --> 00:05:02,100 This mutable borrow lasts until the end of the line. 87 00:05:02,720 --> 00:05:08,370 The immutable borrow of self that score takes happens at the same time that the mutable borrow is active, 88 00:05:08,510 --> 00:05:12,080 which isn't allowed. To fix this, 89 00:05:12,140 --> 00:05:14,649 we can introduce a temporary variable to hold 90 00:05:14,650 --> 00:05:16,050 the result of the expression. 91 00:05:16,640 --> 00:05:18,330 The expression can then be computed, 92 00:05:18,340 --> 00:05:21,650 and the borrow will end before a conflicting borrow starts. 93 00:05:23,540 --> 00:05:24,520 In this example, 94 00:05:24,600 --> 00:05:27,010 the temporary variable will hold the previous score, 95 00:05:27,070 --> 00:05:33,850 so that the immutable borrow of player one from the score method ends before the mutable borrow from set_score begins. 96 00:05:35,340 --> 00:05:37,450 This code now compiles and runs. 97 00:05:38,940 --> 00:05:39,530 Next, 98 00:05:39,650 --> 00:05:43,049 let's look at a method named entry that is a part of some data structure's 99 00:05:43,050 --> 00:05:43,950 APIs. 100 00:05:44,340 --> 00:05:46,360 We're going to show an example using HashMap. 101 00:05:47,940 --> 00:05:50,480 When working with a HashMap of key/value pairs, 102 00:05:50,520 --> 00:05:52,450 it's common to look up a key in the HashMap. 103 00:05:52,940 --> 00:05:54,090 If the key exists, 104 00:05:54,190 --> 00:05:55,570 we want to update the value. 105 00:05:55,790 --> 00:05:56,920 But if it doesn't exist, 106 00:05:57,020 --> 00:05:58,950 we want to insert an initial value. 107 00:06:00,540 --> 00:06:01,390 For example, 108 00:06:01,450 --> 00:06:03,880 to count the frequency of words in some text, 109 00:06:04,340 --> 00:06:07,689 we could have a HashMap where the words would be the keys and their frequencies would be the values. 110 00:06:07,690 --> 00:06:10,730 For each word in the text, 111 00:06:10,740 --> 00:06:11,450 we'd look it up. 112 00:06:12,040 --> 00:06:13,250 If the word was present, 113 00:06:13,640 --> 00:06:18,780 we would increase the value to tally another occurrence of the word. If the word was not present, 114 00:06:19,070 --> 00:06:24,150 this is the first time we've seen the word, so insert it into the HashMap with a corresponding value of 1. 115 00:06:25,640 --> 00:06:29,050 Let's look at an attempt to write this code in Rust that doesn't work. 116 00:06:30,400 --> 00:06:35,250 This code looks up each word from the text in the frequency's HashMap using the get_mut method. 117 00:06:35,940 --> 00:06:37,170 If the word is found, 118 00:06:37,370 --> 00:06:40,850 the Some arm of the match succeeds and we add one to the value. 119 00:06:41,470 --> 00:06:42,930 If the word isn't found, 120 00:06:43,010 --> 00:06:44,570 get_mut returns None, 121 00:06:44,580 --> 00:06:47,529 and we insert the word with the value 1 to record that 122 00:06:47,530 --> 00:06:49,350 this is the first time we've seen this word. 123 00:06:50,840 --> 00:06:52,030 If we try to compile this, 124 00:06:52,170 --> 00:06:56,650 the errors say that we're not allowed to borrow frequencies as mutable more than once at a time. 125 00:06:57,380 --> 00:07:02,850 There shows that the call to get_mut is the first mutable borrow and the call to insert is the second. 126 00:07:03,640 --> 00:07:04,750 So, how do we fix this? 127 00:07:06,330 --> 00:07:09,400 The solution is to use the entry method defined on HashMap. 128 00:07:10,240 --> 00:07:11,649 This method abstracts away 129 00:07:11,650 --> 00:07:19,050 the conditional's that handle whether the key is present or absent, and instead exposes methods to customize what to do in those cases? 130 00:07:20,620 --> 00:07:24,230 The entry method takes a key and returns an instance of the Entry 131 00:07:24,240 --> 00:07:29,510 enum. The Entry enum has two variants: occupied or vacant. 132 00:07:31,040 --> 00:07:39,280 The or_insert method on Entry will return a mutable reference that, for an occupied entry, refers to the existing value. And for a vacant entry, 133 00:07:39,290 --> 00:07:43,950 will insert the given value and then return a mutable reference to the now existing value. 134 00:07:45,500 --> 00:07:47,640 If we change the code to use these methods, 135 00:07:47,760 --> 00:07:49,980 this one line of code will insert 0 136 00:07:49,990 --> 00:07:51,550 if the word is not in the HashMap, 137 00:07:52,040 --> 00:07:55,650 and then we'll add 1 to the entry that was either found or just inserted. 138 00:07:57,180 --> 00:07:59,400 This compiles and counts the word frequencies. 139 00:08:00,880 --> 00:08:01,540 Finally, 140 00:08:01,630 --> 00:08:04,550 let's explore splitting up structs into multiple structs. 141 00:08:06,040 --> 00:08:09,880 Here, we have a game with a Monster struct that has fields for health points, 142 00:08:10,050 --> 00:08:13,300 spell points, and a list of friends who have a loyalty value. 143 00:08:14,010 --> 00:08:16,950 We've defined a method on Monster called final_breath. 144 00:08:17,340 --> 00:08:19,660 If the monster is about to run out of health points, 145 00:08:19,850 --> 00:08:25,790 one of its friends can save it by giving it hp and using some sp in proportion to their loyalty. 146 00:08:26,440 --> 00:08:32,150 Rust understands that it is safe to borrow different fields of the same struct because there's no way that they can conflict. 147 00:08:32,640 --> 00:08:38,929 This method borrows self.friends immutably in the call to self.friends.first and it borrows 148 00:08:38,930 --> 00:08:47,050 self.hp and self.sp mutably when updating those values. This code compiles without any errors. 149 00:08:48,540 --> 00:08:54,450 Now you want to extract a heal method that takes care of updating hp and sp in order to reuse this code. 150 00:09:01,440 --> 00:09:03,040 This code will no longer compile. 151 00:09:03,260 --> 00:09:09,550 We get an error that says, cannot borrow `*self` as mutable because `self.friends` is also borrowed as immutable. 152 00:09:11,180 --> 00:09:16,179 This is because the heal method now takes a mutable reference to all of self, and Rust 153 00:09:16,180 --> 00:09:20,640 doesn't see that the heal method only modifies the hp and sp fields, 154 00:09:20,760 --> 00:09:22,000 not the friend field. 155 00:09:23,540 --> 00:09:24,479 The pattern to solve 156 00:09:24,480 --> 00:09:25,929 this problem is to split up 157 00:09:25,930 --> 00:09:28,759 the Monster struct into smaller structs that group 158 00:09:28,760 --> 00:09:29,419 the fields that are 159 00:09:29,420 --> 00:09:35,050 used together. The methods that only use some of the fields are then defined on the inner struct instead. 160 00:09:36,640 --> 00:09:40,840 Here, we're creating a struct called Stats for the hp and sp fields. 161 00:09:41,090 --> 00:09:43,950 We move the heal method to the Stats struck. 162 00:09:44,900 --> 00:09:47,120 The Monster struct will then have a stats field, 163 00:09:47,430 --> 00:09:55,150 and the final_breath method can call self.stats.heal. This code compiles successfully, and as a side effect, 164 00:09:55,240 --> 00:09:56,850 it's also a better design. 165 00:09:57,340 --> 00:10:00,850 Now we can reuse the Stats struct for something other than monsters. 166 00:10:02,340 --> 00:10:05,450 Now that we've looked at some common Rust borrowing patterns, 167 00:10:05,940 --> 00:10:08,950 let's talk about how these patterns might change in the future. 168 00:10:10,540 --> 00:10:12,280 At the time of recording this video, 169 00:10:12,460 --> 00:10:14,860 there's an improvement to the Rust compiler being developed, 170 00:10:14,870 --> 00:10:16,740 called Non-Lexical Lifetimes 171 00:10:16,960 --> 00:10:17,819 or NLL 172 00:10:17,820 --> 00:10:18,450 for short. 173 00:10:18,940 --> 00:10:21,490 This feature teaches the borrow checker some new tricks. 174 00:10:22,080 --> 00:10:26,550 The borrow checker learns that borrows don't always have to last until the end of the lexical scope. 175 00:10:28,040 --> 00:10:28,809 In this example, 176 00:10:28,810 --> 00:10:31,189 where we didn't use the immutable borrows after the println, 177 00:10:31,190 --> 00:10:31,510 178 00:10:32,140 --> 00:10:37,850 the borrow checker will soon understand that and allow this code to compile without having to add an extra inner scope. 179 00:10:39,240 --> 00:10:43,369 Another capability that the borrow checker will gain is being able to see that a borrow 180 00:10:43,370 --> 00:10:47,050 that's part of an expression can end after computing that part, 181 00:10:47,320 --> 00:10:49,650 rather than hanging around for the entire expression. 182 00:10:51,140 --> 00:10:51,859 In this example, 183 00:10:51,860 --> 00:10:53,750 where we introduced a temporary variable, 184 00:10:54,200 --> 00:11:03,590 the borrow checker will understand that the immutable borrow to player1 can end after getting the score, so that it doesn't occur at the same time as the mutable borrow the set_score takes. 185 00:11:05,240 --> 00:11:12,100 Another improvement will be making the borrow checker understand when borrows aren't used in all arms of an if or match expression. 186 00:11:14,000 --> 00:11:23,550 The initial code we tried in the word frequency example will be allowed because the borrow checker will understand that, in the None arm, there won't be a mutable reference from the call to get_mut. 187 00:11:23,760 --> 00:11:27,150 So there's nothing to conflict with the mutable reference that insert needs. 188 00:11:28,740 --> 00:11:32,150 There's a few things to keep in mind for when these improvements are completed. 189 00:11:32,640 --> 00:11:33,389 Using the Entry 190 00:11:33,390 --> 00:11:36,520 API in the example we looked at won't be necessary, 191 00:11:36,660 --> 00:11:38,550 but it will still be a common pattern. 192 00:11:39,080 --> 00:11:41,850 The Entry API makes code more concise and clear. 193 00:11:43,240 --> 00:11:43,909 The Entry API 194 00:11:43,910 --> 00:11:47,290 is also more efficient. In the code that uses the match, 195 00:11:47,300 --> 00:11:50,890 the hash of the key is computed twice. When using the Entry API, 196 00:11:51,060 --> 00:11:52,750 the hash is only computed once. 197 00:11:54,460 --> 00:11:59,220 The other example that won't change is the monster example, where we split the struct into smaller structs, 198 00:11:59,230 --> 00:12:00,750 based on how the fields are used. 199 00:12:01,240 --> 00:12:05,340 This pattern will still be useful because methods will still borrow the entire struct, 200 00:12:05,570 --> 00:12:07,350 which will count as "at the same time." 201 00:12:08,840 --> 00:12:17,050 Keep in mind that the NLL improvements are fixing cases where the borrow checker was being overly conservative and rejecting code that was actually valid. 202 00:12:17,590 --> 00:12:19,650 It will still reject invalid code. 203 00:12:21,240 --> 00:12:21,980 For instance, 204 00:12:22,050 --> 00:12:28,819 in the example where we tried to take a mutable reference between when immutable references were created and used, the borrows are still happening 205 00:12:28,820 --> 00:12:29,830 at the same time, 206 00:12:30,050 --> 00:12:31,850 no matter how you analyze the code. 207 00:12:32,570 --> 00:12:35,950 This code still won't compile after NLL is done. 208 00:12:37,540 --> 00:12:38,130 Finally, 209 00:12:38,320 --> 00:12:42,020 if you're looking at code written before the NLL improvements were completed, 210 00:12:42,110 --> 00:12:44,950 or that needs to be able to compile with an older version of Rust, 211 00:12:45,340 --> 00:12:48,250 you'll likely still see the patterns we've covered in this module. 212 00:12:48,840 --> 00:12:56,140 It's useful to understand why code with extra scopes and temporary variables was written that way. 213 00:12:56,150 --> 00:13:02,250 In this module, we've discussed that in the context of references, "at the same time" usually means in the same lexical scope. 214 00:13:03,340 --> 00:13:09,650 We looked at common code patterns that introduced new scopes to tell the borrow checker that a reference is no longer needed, 215 00:13:10,070 --> 00:13:13,650 introduced a temporary variable to end one borrow before another, 216 00:13:14,040 --> 00:13:21,450 used the Entry API to update or insert into a HashMap, and split up structs to have methods only borrow relevant fields. 217 00:13:22,340 --> 00:13:31,850 We also went over the non-lexical lifetime improvements coming soon to a compiler near you. In the next module, we'll show how ownership applies to more than just memory.