1 00:00:06,570 --> 00:00:08,940 - The purpose of Rust as a language, really, 2 00:00:08,940 --> 00:00:12,360 is to be a safer version of C++, I think, 3 00:00:12,360 --> 00:00:13,860 that's being honest about it. 4 00:00:13,860 --> 00:00:16,320 Rust as a language, and as a library, 5 00:00:16,320 --> 00:00:18,690 puts in place many layers of protection 6 00:00:18,690 --> 00:00:21,420 to avoid many of the painful and expensive errors 7 00:00:21,420 --> 00:00:23,220 that we could make in C++. 8 00:00:23,220 --> 00:00:24,540 Such as having a null pointer 9 00:00:24,540 --> 00:00:27,570 that we try to dereference, the program crashes, 10 00:00:27,570 --> 00:00:30,120 or memory allocation bug, you know the kind of thing 11 00:00:30,120 --> 00:00:32,910 where you try to delete an object twice 12 00:00:32,910 --> 00:00:34,500 or you forget to delete an object 13 00:00:34,500 --> 00:00:35,850 and you have a memory leak? 14 00:00:35,850 --> 00:00:39,300 These are classic errors in C++. 15 00:00:39,300 --> 00:00:41,190 Very difficult to do these in Rust 16 00:00:41,190 --> 00:00:43,320 because the Rust language and the library 17 00:00:43,320 --> 00:00:44,910 stops you from doing it. 18 00:00:44,910 --> 00:00:46,470 Thread race conditions. 19 00:00:46,470 --> 00:00:50,650 Trying to access static data in C++ would be fine 20 00:00:51,690 --> 00:00:53,160 but it's potentially dangerous, 21 00:00:53,160 --> 00:00:54,510 because multiple threads 22 00:00:54,510 --> 00:00:56,700 could then step on each other's toes. 23 00:00:56,700 --> 00:00:57,540 You need to have some kind 24 00:00:57,540 --> 00:01:01,440 of safety mechanism in place like a mutex for example. 25 00:01:01,440 --> 00:01:04,350 So Rust as a language stops us 26 00:01:04,350 --> 00:01:07,833 from doing things that could damage our code. 27 00:01:09,210 --> 00:01:11,790 But in some occasions this layer of protection 28 00:01:11,790 --> 00:01:13,920 in Rust actually gets in the way. 29 00:01:13,920 --> 00:01:18,240 Maybe we really do need to access direct memory addresses. 30 00:01:18,240 --> 00:01:22,290 Or you know, low level resources in some kind of direct way. 31 00:01:22,290 --> 00:01:25,350 So for example, dereferencing the raw pointer, 32 00:01:25,350 --> 00:01:27,633 updating static shared data, 33 00:01:28,590 --> 00:01:33,360 some kind of register related code. 34 00:01:33,360 --> 00:01:35,940 So in these edge conditions 35 00:01:35,940 --> 00:01:38,850 Rust allows us to opt out of the security mechanism 36 00:01:38,850 --> 00:01:41,580 and to define what's called unsafe code. 37 00:01:41,580 --> 00:01:43,260 When you define unsafe code 38 00:01:43,260 --> 00:01:46,500 it consciously bypasses Rust protection layer 39 00:01:46,500 --> 00:01:48,480 and allows us to do low level operations 40 00:01:48,480 --> 00:01:50,760 without any protection from Rust. 41 00:01:50,760 --> 00:01:52,800 That's a good thing and a bad thing 42 00:01:52,800 --> 00:01:54,690 you're on your own now, you better make sure 43 00:01:54,690 --> 00:01:58,710 that the code is safe, that it behaved properly. 44 00:01:58,710 --> 00:02:02,880 So to define this kind of edge condition operation 45 00:02:02,880 --> 00:02:06,300 you have an unsafe keyword, you define an unsafe block 46 00:02:06,300 --> 00:02:08,550 and in that block you can do anything you like. 47 00:02:08,550 --> 00:02:10,772 Rust will let you do anything. 48 00:02:10,772 --> 00:02:12,450 Basically you can dereference raw pointers 49 00:02:12,450 --> 00:02:15,540 access shared data, anything you want to 50 00:02:15,540 --> 00:02:17,670 without Rust getting in the way. 51 00:02:17,670 --> 00:02:21,153 But be careful because Rust isn't protecting you anymore. 52 00:02:22,050 --> 00:02:23,103 You're on your own. 53 00:02:24,060 --> 00:02:25,980 It's a simple enough concept. 54 00:02:25,980 --> 00:02:27,870 So let's have a look at an example 55 00:02:27,870 --> 00:02:29,310 in the usual project. 56 00:02:29,310 --> 00:02:30,600 And we'll have a look at main 57 00:02:30,600 --> 00:02:33,573 then we'll see demo unsafe code and then we'll run it. 58 00:02:34,590 --> 00:02:37,983 So here we are in the source, main. 59 00:02:39,600 --> 00:02:42,750 That's the demo and here's the code. 60 00:02:42,750 --> 00:02:45,030 So I've got an unsafe block here. 61 00:02:45,030 --> 00:02:46,890 Okay, let me step you through the code. 62 00:02:46,890 --> 00:02:48,630 In fact, I just run it first 63 00:02:48,630 --> 00:02:52,830 because there are some other little things I want to try. 64 00:02:52,830 --> 00:02:53,850 After running through the code 65 00:02:53,850 --> 00:02:55,143 as it currently stands. 66 00:02:57,750 --> 00:02:59,640 Right, so from the top I declare a couple 67 00:02:59,640 --> 00:03:02,190 of normal mutable integers, 68 00:03:02,190 --> 00:03:05,280 x will be 101 after those two statements 69 00:03:05,280 --> 00:03:07,950 and y will be 201. 70 00:03:07,950 --> 00:03:10,440 And then I've declared some raw pointers. 71 00:03:10,440 --> 00:03:12,740 This is the first time we've seen this syntax. 72 00:03:13,770 --> 00:03:16,290 Rust does have raw pointers. 73 00:03:16,290 --> 00:03:19,050 Until now we've been declaring references 74 00:03:19,050 --> 00:03:21,150 which are kind of protected by Rust 75 00:03:21,150 --> 00:03:22,770 and the borrow checker, et cetera. 76 00:03:22,770 --> 00:03:24,060 But you can have raw pointers 77 00:03:24,060 --> 00:03:27,570 which are literally just addresses in memory. 78 00:03:27,570 --> 00:03:31,140 So p1 is a pointer to an integer. 79 00:03:31,140 --> 00:03:33,660 The calls keyword you can imagine it means 80 00:03:33,660 --> 00:03:37,560 that the pointer used in the pointer using p1 81 00:03:37,560 --> 00:03:39,750 it treats the integer as constant. 82 00:03:39,750 --> 00:03:42,540 It's like a pointer to constant data. 83 00:03:42,540 --> 00:03:45,060 X itself could be modified 84 00:03:45,060 --> 00:03:48,870 but using this pointer will treat the data as constant. 85 00:03:48,870 --> 00:03:53,870 So I would say p1 is a pointer to constant data 86 00:03:54,810 --> 00:03:57,120 and you give it the address of x. 87 00:03:57,120 --> 00:04:00,120 So here the ampersand really does mean address. 88 00:04:00,120 --> 00:04:02,580 It doesn't mean reference or borrow. 89 00:04:02,580 --> 00:04:05,790 It means address as it would do in C and C++. 90 00:04:05,790 --> 00:04:08,580 So this code here would be quite familiar 91 00:04:08,580 --> 00:04:10,230 to a C and C++ developer. 92 00:04:10,230 --> 00:04:13,560 Take the address of x, store the address in p1, 93 00:04:13,560 --> 00:04:17,280 and treat the data, x effectively, as constant. 94 00:04:17,280 --> 00:04:18,690 You've got to use 95 00:04:18,690 --> 00:04:22,050 either the const keyword or the mat keyword 96 00:04:22,050 --> 00:04:23,220 when you declare a pointer. 97 00:04:23,220 --> 00:04:25,470 You've gotta consciously say the pointer 98 00:04:25,470 --> 00:04:28,920 will allow the data to change like p2. 99 00:04:28,920 --> 00:04:32,910 P2 is a multiple pointer, or rather, it allows you 100 00:04:32,910 --> 00:04:36,060 to change the underlying data via the pointer. 101 00:04:36,060 --> 00:04:37,620 So you take the address of y, 102 00:04:37,620 --> 00:04:39,300 you have to say mut here as well. 103 00:04:39,300 --> 00:04:42,060 You have to make it clear to the compiler 104 00:04:42,060 --> 00:04:43,350 that you know what you're doing. 105 00:04:43,350 --> 00:04:45,000 Yes, I do know what I'm doing. 106 00:04:45,000 --> 00:04:47,100 I am taking a mutable address. 107 00:04:47,100 --> 00:04:50,880 I do envisage the value of y changing via this pointer. 108 00:04:50,880 --> 00:04:53,400 Take the address and allow you to change the value of 109 00:04:53,400 --> 00:04:56,130 and you can store that in a mutable pointer. 110 00:04:56,130 --> 00:04:58,680 Okay? So in the first case, p1, 111 00:04:58,680 --> 00:05:01,680 when you say star const, it designates a roll pointer 112 00:05:01,680 --> 00:05:04,440 which treats the underlying data as constant. 113 00:05:04,440 --> 00:05:07,290 And then when you say star mut, you declare 114 00:05:07,290 --> 00:05:10,440 you designate roll pointer that treats the underlying data 115 00:05:10,440 --> 00:05:12,210 as potentially mutable. 116 00:05:12,210 --> 00:05:17,210 So interestingly, declaring pointers isn't unsafe. 117 00:05:17,340 --> 00:05:19,170 You can declare pointers and that's fine. 118 00:05:19,170 --> 00:05:21,240 It's only when you dereference the pointer 119 00:05:21,240 --> 00:05:23,160 that it's potentially dangerous. 120 00:05:23,160 --> 00:05:25,890 So I can declare my pointers in normal code 121 00:05:25,890 --> 00:05:28,350 but to dereference the pointers, I have to do that 122 00:05:28,350 --> 00:05:31,170 in an unsafe block or an unsafe function. 123 00:05:31,170 --> 00:05:33,870 So I dereference p1, 124 00:05:33,870 --> 00:05:35,790 and it'll give me the contents of p1. 125 00:05:35,790 --> 00:05:38,010 In other words, the contents of x really 126 00:05:38,010 --> 00:05:41,100 and the contents of x is 101. 127 00:05:41,100 --> 00:05:43,530 I can't do this. 128 00:05:43,530 --> 00:05:45,420 I can't change the underlying value 129 00:05:45,420 --> 00:05:49,410 because p1 promises to treat the data as constant. 130 00:05:49,410 --> 00:05:51,780 I can't then try to change the underlying data. 131 00:05:51,780 --> 00:05:54,540 I mean syntactically, that would be the right syntax 132 00:05:54,540 --> 00:05:56,370 but I can't actually change the value 133 00:05:56,370 --> 00:05:59,700 because p1 promised to treat the data as constant. 134 00:05:59,700 --> 00:06:01,653 So that would gimme a compiler error. 135 00:06:02,520 --> 00:06:03,960 I'm gonna prove the point. 136 00:06:03,960 --> 00:06:06,753 Let's uncomment that code and run it again. 137 00:06:08,010 --> 00:06:11,150 And let's see, right, on line 20, 138 00:06:11,150 --> 00:06:13,380 p1 is a star cons pointer. 139 00:06:13,380 --> 00:06:16,170 So the data it refers to cannot be written. 140 00:06:16,170 --> 00:06:18,000 Okay, clear enough. 141 00:06:18,000 --> 00:06:20,400 So don't do that. 142 00:06:20,400 --> 00:06:23,430 I can change the value that p2 points two obviously 143 00:06:23,430 --> 00:06:26,910 because p2 is a mutable pointer 144 00:06:26,910 --> 00:06:29,310 so that allows me to change the value. 145 00:06:29,310 --> 00:06:32,133 So y afterwards will be 222. 146 00:06:33,000 --> 00:06:35,250 So I guess what I could do is I could print 147 00:06:35,250 --> 00:06:37,110 the value of y afterwards as well. 148 00:06:37,110 --> 00:06:38,460 That would be quite a good proof, wouldn't it? 149 00:06:38,460 --> 00:06:41,280 To say that the value of y is actually what's changed. 150 00:06:41,280 --> 00:06:43,440 So we say println!, 151 00:06:43,440 --> 00:06:48,440 let's print the value of y afterwards and why not? 152 00:06:48,990 --> 00:06:51,810 So this will change the value the p2 points to, 153 00:06:51,810 --> 00:06:55,953 which is y. Y was 201, 154 00:06:56,820 --> 00:06:58,817 y will become 222, 155 00:06:58,817 --> 00:07:03,063 and we can verify the y really is 222 afterwards. 156 00:07:08,220 --> 00:07:11,580 Okay, so p2 really does point to y 157 00:07:11,580 --> 00:07:13,590 and y really has changed afterwards. 158 00:07:13,590 --> 00:07:16,500 I guess one last thing I'm going to do is to 159 00:07:16,500 --> 00:07:20,100 try to do all this pointer related manipulation outside 160 00:07:20,100 --> 00:07:21,477 of an unsafe block. 161 00:07:21,477 --> 00:07:22,623 And you can't. 162 00:07:23,640 --> 00:07:27,720 Okay, so, if I move that code to the outer scope 163 00:07:27,720 --> 00:07:31,020 and I try to dereference pointers in normal code 164 00:07:31,020 --> 00:07:34,110 at that point we were in full Rust compiler mode. 165 00:07:34,110 --> 00:07:35,253 It won't let us do it. 166 00:07:38,850 --> 00:07:41,670 Loads of errors. Good. 167 00:07:41,670 --> 00:07:44,610 Because I'm trying to do something that's pretty dangerous. 168 00:07:44,610 --> 00:07:47,190 I'm trying to dereference a raw pointer. 169 00:07:47,190 --> 00:07:49,260 The raw pointer could be null. 170 00:07:49,260 --> 00:07:52,800 Or a dangling pointer or unaligned. 171 00:07:52,800 --> 00:07:54,570 This could basically cause us 172 00:07:54,570 --> 00:07:56,640 to have a very bad day in the office. 173 00:07:56,640 --> 00:07:59,790 So you can't dereference raw pointers. 174 00:07:59,790 --> 00:08:03,150 it has to go in an unsafe block like that. 175 00:08:03,150 --> 00:08:05,430 Okay, so I'll reinstate the unsafe block 176 00:08:05,430 --> 00:08:07,650 and then just for due diligence 177 00:08:07,650 --> 00:08:09,270 I'm gonna run the application again just to prove 178 00:08:09,270 --> 00:08:11,760 that it works and it does. 179 00:08:11,760 --> 00:08:16,380 So obviously using unsafe code is something that you do 180 00:08:16,380 --> 00:08:19,440 because you have to, you try to avoid it where possible 181 00:08:19,440 --> 00:08:22,770 because you are taking life in your own hands here. 182 00:08:22,770 --> 00:08:25,080 So use it when you need to 183 00:08:25,080 --> 00:08:27,270 but only when you really need to. 184 00:08:27,270 --> 00:08:30,180 Most times avoid using unsafe code. 185 00:08:30,180 --> 00:08:32,400 The Rust compiler is there for your benefit 186 00:08:32,400 --> 00:08:33,240 but it gives you this kind 187 00:08:33,240 --> 00:08:35,973 of escape route if you really need it.