1 00:00:06,600 --> 00:00:08,970 - In the previous section, I introduced you 2 00:00:08,970 --> 00:00:12,180 to the box structure and box acts 3 00:00:12,180 --> 00:00:14,580 as a smart pointer to an object on the heap. 4 00:00:14,580 --> 00:00:19,580 So a box has a pointer to an object on the heap like that. 5 00:00:20,850 --> 00:00:23,370 When a box object goes out of scope, 6 00:00:23,370 --> 00:00:26,880 the heap object is deallocated automatically. 7 00:00:26,880 --> 00:00:30,390 So the drop function on box 8 00:00:30,390 --> 00:00:32,940 will deallocate the object on the heap 9 00:00:32,940 --> 00:00:36,600 and then the box object itself is popped off the stack. 10 00:00:36,600 --> 00:00:41,340 So it's a bit like auto pointer in C++, 11 00:00:41,340 --> 00:00:44,820 in the original C++ upgrade. 12 00:00:44,820 --> 00:00:48,120 So there's also a type called Rc 13 00:00:48,120 --> 00:00:51,810 in Rust, reference counted pointer basically. 14 00:00:51,810 --> 00:00:53,460 It allocates an object on the heap 15 00:00:53,460 --> 00:00:55,890 but it also maintains a reference count. 16 00:00:55,890 --> 00:01:00,690 So an Rc object will have a pointer 17 00:01:00,690 --> 00:01:04,740 to an object on the heap, which is dynamically allocated. 18 00:01:04,740 --> 00:01:09,120 And effectively, it also has a reference counter like so. 19 00:01:09,120 --> 00:01:14,120 This is similar to, very similar indeed to shared pointer 20 00:01:14,520 --> 00:01:18,540 if you're familiar with that in standard C++. 21 00:01:18,540 --> 00:01:20,280 So you can clone an Rc. 22 00:01:20,280 --> 00:01:22,330 Rc implements the clone trait 23 00:01:23,340 --> 00:01:25,410 and you can imagine what the clone function does. 24 00:01:25,410 --> 00:01:28,980 It's like the copy constructor for shared pointer in C++ 25 00:01:28,980 --> 00:01:30,660 if you're familiar with that. 26 00:01:30,660 --> 00:01:32,860 It increments the reference counter. 27 00:01:32,860 --> 00:01:35,103 So if you say Rc1 equals Rc2, 28 00:01:35,940 --> 00:01:40,740 then this Rc, this is Rc1, and this is Rc2, 29 00:01:40,740 --> 00:01:43,050 it'll point to the same place, 30 00:01:43,050 --> 00:01:44,970 it'll point to the same reference counter 31 00:01:44,970 --> 00:01:48,243 and the reference counter gets incremented to be two. 32 00:01:49,320 --> 00:01:52,230 When an Rc object goes out of scope, 33 00:01:52,230 --> 00:01:53,910 its drop function is called, 34 00:01:53,910 --> 00:01:56,880 which is like a distractor in a shared pointer. 35 00:01:56,880 --> 00:01:59,793 So let's say Rc2 goes at a scope. 36 00:02:00,630 --> 00:02:02,098 At that point, 37 00:02:02,098 --> 00:02:07,098 it will decrement the reference counter like so. 38 00:02:09,030 --> 00:02:10,620 If the reference counter was zero, 39 00:02:10,620 --> 00:02:12,510 then the underlying object would be deleted. 40 00:02:12,510 --> 00:02:13,680 It isn't zero. 41 00:02:13,680 --> 00:02:16,290 So the object still exists on the heap, 42 00:02:16,290 --> 00:02:18,870 but when Rc1 goes out of scope, 43 00:02:18,870 --> 00:02:21,273 it'll document the reference counter to zero. 44 00:02:22,500 --> 00:02:24,330 And because the reference count's zero, 45 00:02:24,330 --> 00:02:26,970 it means I'm the last surviving owner 46 00:02:26,970 --> 00:02:30,150 of the object and the object itself is deallocated. 47 00:02:30,150 --> 00:02:33,540 And then obviously, the Rc goes out of scope. 48 00:02:33,540 --> 00:02:36,060 So it's a reference counted smart pointer, 49 00:02:36,060 --> 00:02:40,080 just like shared pointer in C++. 50 00:02:40,080 --> 00:02:41,910 So there we go. 51 00:02:41,910 --> 00:02:45,030 Let's have a look at an example in the usual project. 52 00:02:45,030 --> 00:02:46,800 We'll have a look at main.rs 53 00:02:46,800 --> 00:02:50,520 and then we'll have a look at demo_rc and then we'll run it. 54 00:02:50,520 --> 00:02:55,520 Okay, so in main, let's uncomment the code for this demo. 55 00:02:55,800 --> 00:02:57,660 And here is the demo. 56 00:02:57,660 --> 00:02:59,460 I'm going to run it first 57 00:02:59,460 --> 00:03:01,590 because you need to see the output 58 00:03:01,590 --> 00:03:02,973 for the code to make sense. 59 00:03:08,610 --> 00:03:10,500 So what I'll do is I'll step through the code 60 00:03:10,500 --> 00:03:13,710 and I'll kind of explain the output step by step. 61 00:03:13,710 --> 00:03:14,730 So first of all, 62 00:03:14,730 --> 00:03:19,710 I've imported the Rc type from the std::rc module. 63 00:03:19,710 --> 00:03:22,110 I define a simple Employee object structure. 64 00:03:22,110 --> 00:03:24,300 That's the object I'm gonna allocate on heap 65 00:03:24,300 --> 00:03:25,950 via a reference counter. 66 00:03:25,950 --> 00:03:29,490 So you say Rc::new, and then you give it the object, 67 00:03:29,490 --> 00:03:33,420 an Employee and that object gets allocated on the heap. 68 00:03:33,420 --> 00:03:36,180 And the reference counter, that is one initially. 69 00:03:36,180 --> 00:03:41,180 So if I output the Rc structure has an associated function, 70 00:03:42,000 --> 00:03:44,102 static method basically. 71 00:03:44,102 --> 00:03:46,350 Strong_count, interesting term, isn't it? 72 00:03:46,350 --> 00:03:47,250 Strong_count. 73 00:03:47,250 --> 00:03:50,280 That suggests maybe the concept of a weak count as well. 74 00:03:50,280 --> 00:03:51,720 Anyway, the strong_count 75 00:03:51,720 --> 00:03:55,530 is how many references do we have to the object? 76 00:03:55,530 --> 00:03:57,420 What's the current reference count? 77 00:03:57,420 --> 00:04:00,510 The current reference count initially is one, 78 00:04:00,510 --> 00:04:02,220 which is what we'd expect. 79 00:04:02,220 --> 00:04:05,560 So then I call the clone method Rc::clone 80 00:04:06,420 --> 00:04:08,430 and I give it my object. 81 00:04:08,430 --> 00:04:11,700 And notice, it's a static method on Rc. 82 00:04:11,700 --> 00:04:14,850 You don't say Employee object.clone. 83 00:04:14,850 --> 00:04:16,200 Obviously, you're not cloning the object, 84 00:04:16,200 --> 00:04:18,510 you're cloning the reference. 85 00:04:18,510 --> 00:04:22,560 So when you clone the reference count on a, 86 00:04:22,560 --> 00:04:24,900 then output the reference count. 87 00:04:24,900 --> 00:04:28,533 The reference count now is going to be two. 88 00:04:29,730 --> 00:04:34,530 Now then, I've passed my reference count into a method, 89 00:04:34,530 --> 00:04:37,353 a function by reference. 90 00:04:38,430 --> 00:04:40,560 Reference count, so the Rc structure type 91 00:04:40,560 --> 00:04:42,810 doesn't implement copy. 92 00:04:42,810 --> 00:04:46,713 So if I just pass this like so, 93 00:04:46,713 --> 00:04:49,320 then it would move the reference count 94 00:04:49,320 --> 00:04:51,750 into the function into here 95 00:04:51,750 --> 00:04:54,300 and this function would then own the reference count. 96 00:04:54,300 --> 00:04:56,250 It would mean I couldn't then use the reference count, 97 00:04:56,250 --> 00:04:58,350 the Rc object afterwards. 98 00:04:58,350 --> 00:05:00,330 So we don't want to do that. 99 00:05:00,330 --> 00:05:02,550 You don't want to move the object. 100 00:05:02,550 --> 00:05:04,470 I'm going to pass it by reference. 101 00:05:04,470 --> 00:05:07,350 The alternative would be to clone the reference count again 102 00:05:07,350 --> 00:05:10,560 in the function, but I'm gonna pass by reference. 103 00:05:10,560 --> 00:05:13,233 So when I pass by reference like so, 104 00:05:14,100 --> 00:05:19,083 rc_emp will be a reference to a reference count, 105 00:05:20,610 --> 00:05:23,850 which internally points to an employee. 106 00:05:23,850 --> 00:05:26,100 And inside here, I do actually clone, 107 00:05:26,100 --> 00:05:29,250 I clone my parameter, okay? 108 00:05:29,250 --> 00:05:33,810 So that, when I then output the reference count inside here, 109 00:05:33,810 --> 00:05:37,170 the reference count will be incremented. 110 00:05:37,170 --> 00:05:38,283 So that's value three. 111 00:05:39,270 --> 00:05:43,110 Right, now this cloned reference count is local 112 00:05:43,110 --> 00:05:45,660 and it'll go out of scope at the end of the function. 113 00:05:45,660 --> 00:05:48,180 When it goes out of scope, it'll be dropped. 114 00:05:48,180 --> 00:05:50,640 And the underlying reference, 115 00:05:50,640 --> 00:05:53,070 the underlying thing that we're referring to 116 00:05:53,070 --> 00:05:55,860 will have its counter decremented. 117 00:05:55,860 --> 00:05:57,600 So when I come back up here 118 00:05:57,600 --> 00:06:01,380 into my kind of driver code, after the function returns, 119 00:06:01,380 --> 00:06:06,300 the reference count is backed up to two as you can see 120 00:06:06,300 --> 00:06:07,620 in this piece of code here. 121 00:06:07,620 --> 00:06:09,720 And then I've got a block scope. 122 00:06:09,720 --> 00:06:13,170 In that block scope, I create a reference count, 123 00:06:13,170 --> 00:06:14,850 another reference count, a clone, 124 00:06:14,850 --> 00:06:17,073 so it'll bump the count back up to three. 125 00:06:19,080 --> 00:06:21,540 But obviously, it's a local variable, 126 00:06:21,540 --> 00:06:22,980 a block scope variable. 127 00:06:22,980 --> 00:06:24,690 So it goes at the scope at that point 128 00:06:24,690 --> 00:06:27,630 in which case the reference count comes down to two again. 129 00:06:27,630 --> 00:06:30,000 So if I output the reference count here, 130 00:06:30,000 --> 00:06:31,083 it'll go down to two. 131 00:06:33,510 --> 00:06:36,960 Okay, so it's very similar to shared pointer. 132 00:06:36,960 --> 00:06:40,050 You can use this where you want to pass objects around 133 00:06:40,050 --> 00:06:41,340 and you're not sure 134 00:06:41,340 --> 00:06:44,730 if you know when the time's ready to deallocate the object. 135 00:06:44,730 --> 00:06:47,550 You leave the reference counter, keep count 136 00:06:47,550 --> 00:06:51,000 and when the last surviving reference count disappears, 137 00:06:51,000 --> 00:06:53,730 at that point, the object will be deallocated. 138 00:06:53,730 --> 00:06:55,410 Okay, so there we are. 139 00:06:55,410 --> 00:06:58,650 This Rc class is basically the same 140 00:06:58,650 --> 00:07:03,240 as shared pointer in purpose in C++. 141 00:07:03,240 --> 00:07:04,620 If you're familiar with C++, 142 00:07:04,620 --> 00:07:07,500 you'll also know that as well as a shared pointer, 143 00:07:07,500 --> 00:07:10,560 there's also a smart pointer called weak pointer 144 00:07:10,560 --> 00:07:12,930 and so there is in Rust. 145 00:07:12,930 --> 00:07:14,973 There's a structure called weak. 146 00:07:16,140 --> 00:07:19,260 So weak is a version of Rc, 147 00:07:19,260 --> 00:07:21,180 which holds a non-owning reference 148 00:07:21,180 --> 00:07:23,760 to the managed allocation in order to, 149 00:07:23,760 --> 00:07:26,190 it's basically a kind of like a... 150 00:07:26,190 --> 00:07:28,110 It knows which object you're referring to, 151 00:07:28,110 --> 00:07:31,530 but it doesn't participate in reference counting. 152 00:07:31,530 --> 00:07:33,300 So it's a weak pointer. 153 00:07:33,300 --> 00:07:34,980 In order to use a weak pointer, 154 00:07:34,980 --> 00:07:37,230 you have to upgrade the weak pointer 155 00:07:37,230 --> 00:07:39,780 into a proper shared pointer if you like. 156 00:07:39,780 --> 00:07:43,350 This is a bit like when you log a shared pointer in C++. 157 00:07:43,350 --> 00:07:46,590 So the weak pointer has an upgrade mechanism, 158 00:07:46,590 --> 00:07:49,320 which allows you to say, I want now to convert myself 159 00:07:49,320 --> 00:07:52,890 into a strong reference count, which will participate 160 00:07:52,890 --> 00:07:54,810 in the actual reference counting mechanism. 161 00:07:54,810 --> 00:07:56,970 So the reason for using weak pointers, 162 00:07:56,970 --> 00:07:59,970 if you have a shared pointer and you want to observe it 163 00:07:59,970 --> 00:08:02,010 but you're not ready to actually use it yet, 164 00:08:02,010 --> 00:08:03,660 you can use a weak pointer 165 00:08:03,660 --> 00:08:05,370 and you can also use a weak pointer 166 00:08:05,370 --> 00:08:07,350 to avoid circular references. 167 00:08:07,350 --> 00:08:08,760 An example of that would be 168 00:08:08,760 --> 00:08:13,410 in a binary tree where you have a parent which points 169 00:08:13,410 --> 00:08:17,580 to its child and the child points back to its parent, okay? 170 00:08:17,580 --> 00:08:19,800 They can't both be Rcs 171 00:08:19,800 --> 00:08:22,350 because they would form a closed circuit 172 00:08:22,350 --> 00:08:23,880 and they would never release each other 173 00:08:23,880 --> 00:08:25,470 because they refer to each other. 174 00:08:25,470 --> 00:08:28,380 So the parent might hold a strong pointer, 175 00:08:28,380 --> 00:08:32,250 an Rc to its child, and the child might have a weak pointer 176 00:08:32,250 --> 00:08:34,020 to its parent so that we don't get this kind 177 00:08:34,020 --> 00:08:37,350 of circular loop of strong pointers. 178 00:08:37,350 --> 00:08:39,270 There we are. I'll mention one other thing. 179 00:08:39,270 --> 00:08:44,270 The Rc type can't be used in a multithreaded environment. 180 00:08:45,870 --> 00:08:50,520 You can't send an Rc into a thread. 181 00:08:50,520 --> 00:08:53,130 Technically, there's a trait called send 182 00:08:53,130 --> 00:08:55,320 and only if types implement send 183 00:08:55,320 --> 00:08:57,900 can they be passed into a thread. 184 00:08:57,900 --> 00:08:59,550 So it's not thread safe. 185 00:08:59,550 --> 00:09:03,060 There is another related class called Arc, 186 00:09:03,060 --> 00:09:05,850 atomic reference count, and that's thread safe. 187 00:09:05,850 --> 00:09:09,120 Just like we have i32, which isn't thread safe 188 00:09:09,120 --> 00:09:12,180 and then we have atomic i32, which is thread safe. 189 00:09:12,180 --> 00:09:13,770 It locks itself when you increment 190 00:09:13,770 --> 00:09:15,150 and decrement and so on. 191 00:09:15,150 --> 00:09:19,590 Similarly, Rc isn't thread safe, but the Arc, 192 00:09:19,590 --> 00:09:23,040 Arc, atomic reference code is a thread-safe version 193 00:09:23,040 --> 00:09:25,290 of Rc and that's what you should use 194 00:09:25,290 --> 00:09:26,852 if you want to pass it into a thread. 195 00:09:26,852 --> 00:09:28,701 Very similar API. 196 00:09:28,701 --> 00:09:30,750 It's just that it automatically performs lock in 197 00:09:30,750 --> 00:09:31,650 when you use it. 198 00:09:31,650 --> 00:09:33,180 Okay, so have a look at Arc 199 00:09:33,180 --> 00:09:36,273 just to round off your understanding of how this all works.