About this video
What You'll Learn
- Practice Rustlings control flow exercises covering if/else, match, and return-value style in practical snippets.
- Understand Rust ownership behavior by walking through move semantics, mutability, shadowing, copying, and cloning examples.
- Use Option and enums in real code, including Some/None handling, if let/while let, and match patterns.
Part two of the Rustlings walkthrough with Rust core team member Manish Goregaokar. We cover if/else, match, move semantics and borrowing, Option
Jump to a chapter
- 0:00 Holding
- 0:30 Introductions
- 1:09 Introduction
- 1:49 Guest Introduction
- 2:05 Manish's Background and Rust Journey
- 3:32 Introduction to Rustlings Workflow and First Exercise (if1)
- 4:00 If statements / conditionals
- 4:06 Solving Conditional If (if1)
- 5:12 Rust If/Else Syntax and Implicit Return
- 7:28 Idiomatic Rust & Ternary Operator Discussion
- 9:09 Solving Conditional If (if2)
- 11:00 Match statements
- 11:08 Introduction to Match Statements
- 13:00 Move semantics
- 29:00 Options
- 51:00 Enums
- 1:06:00 Clippy
- 1:12:41 Skipping Primitive Types
- 1:13:00 Tests
- 1:13:02 Introduction to Move Semantics (Part 1: Mutability)
- 1:14:31 Understanding Move Semantics & The Copy Trait
- 1:15:13 Explaining Move Semantics & Cloning
- 1:23:39 Move Semantics: Mutability & Shadowing
- 1:26:30 Move Semantics: Refactoring for Idiomatic Rust
- 1:29:19 Introduction to Options (option1)
- 1:29:42 Understanding Option<T>, Some, and None
- 1:31:07 Wrapping Values in Some & Enum Variants
- 1:32:45 The unwrap() Function & Panics
- 1:33:22 Array Initialization & Type Annotations
- 1:34:26 Handling Uninitialized Data Errors
- 1:35:14 Type Inference vs. Explicit Type Annotation
- 1:39:42 Options: If Let and While Let (option2)
- 1:41:17 Explaining If Let (Conditional Pattern Matching)
- 1:45:07 Handling Nested Options (Option<Option<T>>)
- 1:46:53 Explaining While Let (Iterative Pattern Matching)
- 1:51:08 Introduction to Enums (enums1)
- 1:51:31 Defining Basic Enum Variants
- 1:53:13 println! Formatting: Debug {:?} vs Display {}
- 1:55:53 Enum Variants with Data (enums2)
- 1:56:52 Tuple Variants vs. Struct Variants
- 2:00:42 Pattern Matching Enums (enums3)
- 2:02:22 Defining Enums and Structs
- 2:03:40 Implementing Match for Enum Variants
- 2:07:00 Introduction to Clippy (Rust Linter)
- 2:07:31 Clippy: Comparing Floats (clippy1)
- 2:11:00 Clippy: Iterating Over Options (clippy2)
- 2:13:05 Introduction to Testing (tests1, tests2, tests3)
- 2:13:19 Assert Macros (assert!, assert_eq!, assert_ne!)
- 2:14:38 Conditional Compilation for Tests (cfg(test))
- 2:18:36 Writing Unit Tests
- 2:20:45 Conclusion & Thanks
Full transcript
Generated from the English captions. Timestamps jump the player to that moment.
Read the full transcript
1:09 Introduction
1:09 Hello, and welcome to today's episode of Rawkode live. I'm your host, Rawkode. Before we get started, I just wanna take ten seconds to say thank you to my employer, Equinix Metal. Equinix Metal is a bare metal cloud, and they actually encouraged me to keep producing that series and and all producing quality cloud native content where we can all learn together. So, you know, thank you to them. If you wanna check out Equinix Medal, please use the code Rawkode dash live. This will get you $50 in credit, which is roughly around a hundred hours of bare metal
1:40 compute time. Alright. Today, we're gonna be taking a look at Rust again. This is part two. We've been walking through the Rustlands exercises. Today, I am joined by Manish Gorekakar, a Rust core team member and the lead of the Rust dev tools. Hey, Manish. How are you? I'm doing great. How are you? Awesome. Very well. Thank you. I'm looking forward to learning more Rust today. So, you know, definitely a good day. Do you wanna just give us a little bit of an introduction about yourself? Tell us a little bit more about you. Sure. I mean, I I've been doing Rust since
2:05 Manish's Background and Rust Journey
2:14 2013, '20 '14 before it was stable when Rust used to break all the time. It was not really a great time, but it was, pretty fun to get involved, like, back then when there was a lot of lot of chance to make some impact on the language. For the last four years, I've been, working at Mozilla on the Stylo web browser sorry, the Servo web browser. Stylo was a part of that. And, actually, starting next week, I'm gonna be working at Google on their internationalization team doing a Rust internationalization stuff. Nice. Very cool. Yeah. Okay. So
2:56 I guess I should ask first. I mean, have you've been using Rust a long time. So you're probably not that familiar with the Rustler's exercises. Have you done them before? I have gone through them at some point. I've poked around. I've suggested them to many people, but I've not gone through them, like, bit by bit myself. I've done, like, a couple and, like, read through them. But yeah. Cool. Well, there have I, so this should be fun. Right. Let's oh, we have a hello. Hey, Sangam. Nice to meet you. Hey. So if anyone wants to do this on their
3:32 Introduction to Rustlings Workflow and First Exercise (if1)
3:34 own time, these are great exercises. They've been recommended to me many times. So this is why we're doing this today. They're Rust dash Lang slash Rustlings. We did do a few of them the other day. Jesus. That was Tuesday. Yeah. There we go. This is Friday. That was Tuesday. The way this works is I run Rustling's watch. It's gonna give me a few text and say, yep, we did variables, we did functions. I don't know why functions isn't there, but it doesn't matter. And so they're telling us that the next exercise we wanna attempt is conditional slash f statements. So we'll start
4:06 Solving Conditional If (if1)
4:06 there. We may jump around a little bit and we'll just see what happens. We are using Versus Code live share today. So Manish and I can both see the same code. You also have the ability to type and fix my very broken code, I'm sure. And we'll see what happens. So if you doesn't catch us on Tuesday, the format for these rustling seems to be that we get some sort of test. We then have something else that needs completed for those tests to pass. And then when we're happy with that, we have to remove this I am done. Why
4:36 am I telling you we have to remove the I am done? Because I will probably forget. So, you know, I'll need a little reminder, I'm sure. If we take a look at these tests Okay. So we have some sort of assertion that 10 and then we get a bigger function which should return. Alright. This seems trivial. This is a nice easy start. Right? This just wants me to do an f statement to check for which number is bigger between a and b. Awesome. Okay. I remember correctly from Tuesday's session, the curly brackets are optional in Rust. Is that right?
5:12 Rust If/Else Syntax and Implicit Return
5:17 The the parentheses in if and four are optional. The curly brackets are mandatory. Oh, yeah. Yeah. Okay. Opposite of what, like, JavaScript and c do. This is sometimes confusing. Okay. So it's I start I try it. It's like Golang in this respect. Yes. Okay. Okay. So we can say f a is larger than b. Oh, and then I remembered this. So Yes. You don't have to write the return if it's at the end. Yeah. I really like them as them bindings of Versus code. They're so annoying that every time I get, like, a complete here, I don't
5:53 know how to make it go away. Like, if I push up and down left and right. There we go. See, you're solving problems. Now does this code look okay to you? Looks fine to me. Alright. And so a is better than b, return b. Now if we can just recap some things for anyone who wasn't paying attention or wasn't even watching on Tuesday. This syntax puts out a semicolon, can you give me a refresher of of what that means? So basically, in Rust, at the if you have, like, a block and you can do, like, I don't know,
6:30 some work in the block, and the last line, if you say, like I don't know. If I say five with a semicolon, this means that the block doesn't evaluate to any value. But if I don't have a semicolon at the end of a block, I can actually do, like, x equal to this. And now the value of x is gonna be five after doing all these things, which means that if you want to return from a function, you can just put, like, five here, and it's gonna return five at the when when it reaches the end of a function as
7:01 long as you omit the semicolon. Similarly, you can nest that. So now what we're doing is you can see the highlight. Yeah. Yeah. You can. We are returning this since there's no semicolon after the if, this function is returning this the value of this if block. And the value of this if block is either a or b because we have omitted the semicolons at the end of each of these blocks. Ah, okay. So I'm I'm curious. You know, you've got a lot of experience here. Is this idiomatic Rust or would you do, like, an early return here? Like, if I
7:28 Idiomatic Rust & Ternary Operator Discussion
7:35 mean, if you were writing this code for yourself, you're you're happy with that. This is good. Right? This is idiomatic. Some people prefer doing explicit returns. Like, there's some preferences. But, typically, it is very idiomatic to do use the use the remove not you don't use a semicolon for a return at the end of the blocks and tags. Okay. Like, it's it's very idiomatic to do that because then you can kind of just look at the end of the function and be like, this is what the function is. If a greater than b, it's a, else
8:04 it's b. It's easier to read. Is there a nicer syntax for that? I'm thinking about other languages that have, like, a turn a a ternary operator where I say greater than b. Like, is is there a way of the imagine Rust without the We don't have that operator. We use the question mark for something else as well. And and we don't have the operator in part because this works, so you can just write that if statement. Python does the same thing. Like, I mean, in Python in Rust, you you would just write if you wanted, like, a one line
8:33 turner, you just write if a greater than b a l s b, which is kind of weird. But in Python, you'd also do, like, a if a greater than b l s b, which is similar. But yeah. It's it's we don't have a ternary operator, but we do have this, which is close enough in most cases. Okay. I'll take it. Alright. And I'm just making sure the test cases here don't return two numbers as the same. That would be mean, but it doesn't look like they do. So Yeah. Let's see. Did that pass? It did. So we can
9:09 Solving Conditional If (if2)
9:09 then move our I am done, and it should move us on to f two. There we go. Alright. Let's see what we got. We got test for a. Expects a string and we have an integer. A naive implementation, I can just do that to fix the first problem. Right? Yep. But then my tests fail because we're supposed to be running or returning baz or par. Okay. So get the bar for fuzz and default to baz test passing. If I pass fuzz, I get foo. If I pass fuzz, I get bar. I mean, that's it seems a bit weird,
10:17 but I'm gonna just make that pass then. Like so fuzz is foo and fuzz is bar. Yep. Fuzz fuzz bar else. Fuzz. Yeah. Okay. To test There's a small mistake. But Oh, what's my my mistake? You'd you'd need the physicist here as well. Yeah. Yeah. Alright. Okay. This isn't like a switch or a match, but we do have that. K. So so that passes. So this is now not idiomatic Rust. Is that what you're saying there? That's what probably No. No. I was saying that you've forgotten the physic here, but you added it. Would you use a
11:08 Introduction to Match Statements
11:09 match here instead? Oh, for three cases, maybe, maybe not. I would personally use a match. I don't know if that's really more idiomatic. Like, for this is fine, I think, idiomatic wise. I would personally use a match, but yeah. Can you do you mind showing us the match syntax? That would be cool to So match syntax would look like Oh, wait. No. It's backwards. And fuzz. So these are strings. And then that's a wildcard. And this should probably this should probably work. Alright. Let's see. Yeah. Okay. Cool. So the match sent I mean, that looks pretty familiar. Like, a switch
12:09 case and other c derivative style languages Yep. I guess, similar to what I've seen in at least it follows a similar flow to what I've seen in, like, Alexa as well. The underscore is familiar. Nice. Yeah. That's much cleaner. I'd probably probably go with that one. Okay. Let's remove. I am done. For the people that are watching, if you have questions or any syntax that you want us to try and help you with, if you've been trying to learn Rust on your own, feel free to drop that in the comments, and Manish and I will do our
12:36 best to try and cover that syntax too. Okay. What do we got next? Primitive types. If I remember, we actually skipped these. They were a bit derivative. So why don't we pick something else then? Let's see. Anything you think would be fun that you would like to do on this list? I don't know. Like, have you touched any of the moving and borrowing stuff yet? No. But I I would love to to move semantics where we popped up and opened. Yeah. Alright. Okay. Let's get our test passing then. So Rust watch move semantics one. Is it moo oh, I can't spell. Is
13:00 Move semantics
13:30 that the problem? That Let's try Rustling's run. Yeah. That works. Let's do that. Okay. So we have a message here. It says we cannot borrow back one as mutable. It's not declared as mutable. Alright. So that doesn't seem too scary yet. Let's see. Oh, yeah. So this push must be the fail. So lane 13. Yeah. Okay. So that I can fix. I think. Yep. All it wanted was to be. That's not what I was expecting from the move semantics first one. Maybe it's just easiness in. This is easing us in. This is very much easing us in.
14:24 Yeah. In fact, like, this is not a mute this is just a mutability thing. This is not a move semantics yet. Oh, okay. So this one looks this one is definitely another message to do with the move semantics then. Move occurs because vex needle has a type which does not implement the copy trait. Yeah. That seems a bit scarier. So let's see what we're dealing with. So we declare a mutable vector for this function. Yep. Okay. I'm assuming the effect of one push is okay, so the error message is online. Oh, is that 13 again?
15:02 So let's look at the error message closer. Or Yeah. I mean, what we can I what I can what we can do is go through the error message or I can explain what move semantics are first? Yeah. Let's let's cover that. Yeah. Me with the basics. So in Rust, basically, there's some types like numbers which are copyable. So if you have a number and you, like, assign that number to another variable, it'll just make a copy. So now you have two variables with a number in them, and they'll independently change. But some things like so in Rust, a
15:35 vector is allocated on the heap. So there's allocation involved, and it can be arbitrarily large. You really don't wanna copy that around. What you want to do is if if if you assign the vector to a different variable, that variable is now where the vector is. And the way Rust and and to prevent various problems, what Rust does is that it actually stops that vector from being used from the old variable. Rust is very careful about sharing. When things are being shared, it wants that to be explicit. It wants it it uses the ampersand to talk about sharing often, and it wants
16:16 it to be explicit if the shared variable is mutable, immutable, and stuff like that. So what's going on here is that when you so if you look at the fillvec signature, what it does is that it takes the the takes the vector by move. Like, the vector is being moved into the function and then returned, so it's moved out. So vec zero actually vec zero basically left, like is is the the vector left vec zero here. It is no longer belonging to vec zero. It it moved into the function and out into vec one. So vec zero is
17:05 not not a valid binding anymore. There's so we cannot read it over here because it's gone already. It has been moved out. Does that make sense? It does. Yeah. It does make sense. So basically, in this line, we did a move. Yeah. Okay. So whenever we pass a variable to another function, that's considered moved. Is that, like, the borrowing that we always hear about when I read about Rust? It is the opposite or is counterpart of the borrowing. Right. Okay. You can pass a variable to a function in a way that it gets borrowed, and then then that means that the function
17:43 is temporarily looking at it, but we did not do that yet. We passed it to the function in a way that got moved. Oh, so if we passed it as a bottle, then we wouldn't have this error as well. Was that another okay. Is this line 21? Is that something to common in Rust, or is that just to kinda make this code? It's it's yeah. So that that's actually basically, the the variable mutability is not that big a deal. Like, you could turn it on and off, and that's kind of how you'll turn it on. And that what what
18:15 this is basically doing is saying it's creating a new variable that has the same effect of the same name as the old variable, but it's mutable. There's another way of doing this, and I think it was just done so that they didn't have to introduce the syntax. But you can also just say this, and this would have also worked. Yeah. I think I've I've seen that mutability and the function signature before, and I was just curious. So maybe that's was the more idiomatic way. But you're saying that's actually just to not introduce too many new concepts, probably.
18:43 Yeah. It's, yeah, it's probably to not introduce too many new concepts. But this is called re this is called chat rebinding, and this is done pretty often. Like, in Rust, everything's mute immutable by default. So sometimes what people do is if they want something to be mutable, they'll only temporarily make it mutable, and this is how you'll do that. There's there's a different form of temporary immutability that we can get to later, but this this is the the variable itself being mutable is something you can turn on and off very easily. Does that mean that it's also valid to
19:15 do that? Yes. Yes. And that and that makes it immutable again. Right? That would yeah. Because what's what's happening is that you're just creating a new variable named vec that shadows the old variable named vec, and you're moving it from the old vec into the new vec, the compiler easily sees that these are two separate things. But from your point of view, the old ones be like, the moment you name like, you can also do something like let vec equal to five, and now the vec there is now a new variable called vec that's whose value is five, and that vector
19:49 is kinda gone. You can't access it anymore. So, like, that's that's just called shadowing. You can create variables of the same name as old ones, and they will you can just use them normally. And does that take up double the amount of space? Can I consider no? It doesn't. Okay. Well, if you do let at vac equal to vac, no. It is this is this this compiles down to nothing. Alright. Should have compiled down to nothing. Sometimes compilers are weird, but yeah. Okay. So let's see if we can fix this code then. So what we're saying is we have this
20:23 empty vector called vec zero. Yeah. We then pass it into a function, which then means it's now moved. So vec zero is no longer a reference. Right. So I guess we have two. One option. Do we make this a bottle, or do we clone it? I'm not sure. I guess I guess one of the I guess one of the issues here is what do we want the what do we want the like, what do we want the actual semantics of this program to be? Because what we do here matters. So it tells us not to change this
21:07 line. So from my head, we've gotta make sure that back zero is still is still binding to the value. Alright. So so what we can do here is the the easiest way to do this is that we copy that we create a copy of vex zero. Like, so far, we have not had this problem because all the things we've been dealing with have been copyable. That if if you if you just say that let mut vec one equal to fill vec vec zero and it's vec is a number, it'll just copy it. You won't have this
21:39 problem. And what's happening here is that it's what we one way to solve this is just to make a copy. And the way you do that in Rust is called it's called cloning, and cloning is just when you make an explicit deep copy. This, of course, is going to create a new vector. It's gonna take a make a new allocation, do an entire copy of everything in the vector, which is right now nothing. The vector's empty. And then give it to this function. So this function will have us different vector and vex zero will stay its pristine empty self.
22:16 Okay. So I I mean, I do have a couple of, like, hobby projects that I've been trying my best to write in Rust, and then keep getting frustrated. And I do find myself using dot clone just all the time as, like, my escape hatch for things like this. That is totally fine. I think once people understand the I think as as a newcomer, it is fair it's a good idea to use clone a lot. And, actually, I would recommend that even as an advanced Rust programmer, you should probably be using clone a lot. You you'd probably know enough to be able
22:48 to write this code to avoid copies, but you really you really don't need to in many cases. This is often a premature optimization. So if you need to clone, just clone. And, like, if it becomes a performance issue, you can go back and think of how else could you have done this. Like, one way to do this without cloning in this case, by the way, the clone is free because empty vectors aren't actually allocated. But if the if the vector did have things in it, it would not be free. Yeah. That's good to know. So let's stick with the clone approach then.
23:28 I'm glad that you've validated that for me. I always felt like I was cheating every time I did it, but now I'm okay. It's yeah. Let's see. It's happy. Right? Nice. So we've got move semantics three. Let's see what we're dealing with here. So no test. Make me compare without adding new lines. No lines with multiple semicolons necessary. Alright. Okay. So we initialize an empty vector. We do the same thing here. And then this one hasn't printed in back zero, so that should be fine. We then gotta push here, but with no assignment. So the problem is here that sec one
24:21 is no longer at the same location. Is that right? That might vac one equal to fill vac vac zero. Print vac one and vac one dot push. I guess I should read the error message. Because I'm trying to work out, is this vec dot one dot push part of the move semantics because it's a function on a struct? No? No. So this is a function that takes a mutable borrow. Actually, if you I guess, Versus code will do this. If you highlight on the push, like, if you just put your cursor on the push, it
24:54 should do you see the and mute self? Yep. Yeah. That is that is a mutable borrow. And what that is saying is that for the duration of this function, please give me exclusive access to mutate and read this object. And once I'm done with this function, you can go back to having it. So that's where the borrowing comes in. And that, of course, we that's not actually moving it. That's, like, temporarily doing something. So move semantics it it doesn't break the move semantics. Okay. Let me get the actual error message then instead of guessing. That would probably be
25:32 a good start. Okay. So we've got cannot borrow vec as multiple oh, okay. So oh, yeah. The problem's here. Yeah. So the syntax you used showed us was we could do that, but we could also have done Yeah. But we're not allowed to add lines. Yeah. So now we know why it didn't use that syntax in the last case because it wanted to teach us the syntax here. Yeah. There was methods there. Yeah. If let's see how far that gets. That's it. Alright. Okay. So I'm sitting here looking for some sort of move semantic in this
26:09 function that doesn't even exist. No. Okay. No. The Rustlings try not to introduce, like, a billion concepts, like, one after the other. So they it's it's a very smooth ride. Nice. Okay. Let's read the error message before I open the file then. So move semantics four is found macro vac. Okay. Yeah. So missing the exclamation mark. Right? Let's see. Is it? Or or well, okay. So it says use this to invoke the macro. Yeah. And okay. Refactor this code so that instead of having vec zero and creating the vector and function main, we instead create
27:02 it within the fill back. Okay. So Yep. Does the macro need the parens as well? Yeah. It needs some form of yeah. Okay. It needs a parens or yeah. Doesn't matter. Oh, really? Oh, yeah. Rust macros there are some some depicts here, but, like, Rust macros, the ones that are like this one, you can do this. You can even do this. It does not matter, which is why but people use this because this looks like the array syntax. So if you're making a vector, you'll make it look like an array. Also means that if you wanna fix stack
27:35 array, you can just kinda remove that if you want. Alright. Okay. Well, let's see if that's all at once. No. We got more. Okay. Ah, yes. So we're still passing in, you know, so we can. Yeah. We don't need to do that anymore. There we go. This is also more idiomatic Rust because you typically want if if a function is, like, returning something, you want the and and the function needs it freshly created, it's better if the function creates it. There's no need to freshly create it and pass it in. We don't do out parameters and stuff like that.
28:13 Alright. Okay. That's good to know as well. So that's most semantics. That that means I know everything I need to know now about that, which I don't think is really true yet. So There's there's borrowing and I like, we touched a little bit on borrowing. I don't actually know if they're tits, if they're Rustlings for that, but there probably are somewhere. I I I suspect Rustlings in my I in my recollection, Rustling kind of teaches you borrowing bit by bit, like, in pieces instead of, like, having a here is a thing where we'll teach you just borrowing.
28:46 So yeah. Okay. Well, let's pick something else. And then if we don't find anything else that maybe hints towards what the borrowing stuff is, we can always reopen move semantics for later and then just make that function to borrow and kinda go through that process. We don't want it to do a borrow in this case. It's yeah. There but we can we can have examples of borrowing at some point, I think. Alright. Okay. Let's see. Let me just I'll pick one at random. How about options? Sure. Yeah. Alright. So we got option one. Back error message? Okay.
29:00 Options
29:31 So print number function expects an option and it found an integer. Okay. So it's kind of looks like generic syntax. Yes. You can you kinda just maybe walk me through this for a second? And Sure. What this is saying is that there is a type option which has a generic parameter, which means that that type can be it it's a type option that you give another type to to make the actual final type. So you can have an option of an integer. You can have an option of a string. You can have an option of a
30:11 vec. You can have an option of any type in this case. And what we're doing here is we have an option of an integer, which in the standard library, the option type is basically either something or nothing, which which basically is either you it's it's kind of a nullable type. It is either you have an integer or you have what's called none in Rust, or some languages call it null. Rust calls it none. Yeah. Okay. Kinda reminds me of TypeScript. I'm not sure if you're Yeah. Yeah. Right. TypeScript also has I think it's called option
30:47 or maybe. I forgot. Yeah. It's it's it's the it's the same option syntax. Well, very similar at least. Yeah. So the error then being is that when we call this print number function is that we're passing on just an explicit type. So we need to wrap this somehow and Yeah. Option. That just No. Some. So have you done one of the enums exercises yet? I haven't done the enums exercises. No. Should we go there? We could go there. We could go through this first and, like, looking at how options use will make you understand enums better.
31:29 But I think it it I was I was just wondering how how much I should explain. But yeah. So what option is is that the the way it actually exist is oops. Like that. And what it's what it is is that it's either some and it contains a thing of the type that we passed in. So in this case, because it's it's u 16, what it actually is is that this is a u 16, something like that. And and the none is the null case. So what we're doing is we're wrapping it in sum to create an instance of option
32:14 of u 16 here. Alright. Cool. And that's what the unwrap is doing. We're just saying either get me the none back or the u 16 No. Unwrap is actually if it's none, I I unwrap is I didn't don't expect it to be none here. So if it was none and, like, basically, in the program, it it caused a panic. Alright. So I mean, I'm gonna do that now. Oops. There we go. Like, if we did that, it's going to panic. Alright. Let's try that. So option one. Oh, we've got another have to fix fix other things first.
32:53 Okay. Yep. So we got oh, we expect an option here for the number to add. Oh, I guess we didn't fix all of the cases. Oh, yeah. Numbers as an array of option type size five. Right? That's kinda a syntax to send? Yes. Which means this needs to be some number Yes. To add. Yep. And I haven't seen the syntax before. So Yeah. This is this is this is the type, by the way. So if you say let x so so the full, like, let syntax is, like, let x colon u 60 equals five. And, of course, you can have a mut
33:37 here if you want. And this is this is a type. So using a colon, you can put a type in. And, also, you can do you can do well, there's actually if you compile, you'll see that there's a compiler error here. Okay. Let's do that. And that's save. Can can you go back? Actually, I didn't see what the error was. Oh, it it said that I'm gonna run it again just in case I hadn't saved. Yeah. I think it was it. Okay. Yeah. And now we have possibly uninitialized numbers. Yes. Oh, yeah. That's the error you were gonna
34:21 say there. Right? So what we've done so in Rust, you can, but you can do, like, let x and not say what x is and then, like, 10 lines later, say x equal to 10. But you have to do it in a way that Rust knows that x is x cannot be used in this part. Wait. Oh, yeah. X cannot be used in this part, and you have to do it in a way that Rust knows that x is fully initialized at the end. So here, this array, we start off with an uninitialized array, and
34:51 then we kind of just, like, fill it with a for loop. And Rust cannot prove that, like, you're actually creating the full array here. So what you want to do is we can we can just do like that. Oops. That and it is this this part is actually unnecessary now. So this part is a type annotation, and as you've seen in Rust, let statements usually don't need type annotations. In this case, we're adding one it it was there to help us, I think, but and what it's saying is that it's of type option u 16.
35:30 Oh, it's a it's an array a fixed size array of length five of type option u 16, but I think this will also work. And this is a way of quickly initializing an empty array or quickly initializing an array that has the same same thing in its value. So you can initialize an array of integers like this. You can initialize an array of strings like this, and they will all have the same value. Okay. So what's, you know trying to think about, you know, lessons that I take away from this is idiomatic Rust. Do when you're doing a let
36:09 statement, you rely on the syntax or, you know, do you have a preference towards doing something? When do you include them or not? You include the type when Rust when Rust cannot the idiomatic answer is you include the type when Rust cannot compile without you doing it, which typically occurs in more complicated cases. So, like well, this is but yeah. Yeah. Rust. Yeah. The in this case, Rust knows that this is a a a an a stir. So Rust there's no point telling Rust it's a stir. The only reason you do this if there's some clarity that's lost for the reader,
36:51 and that can happen if you have some really complicated type and you want the reader of the code to know what the type is. But sometimes also you can have some complicated type stuff going on, and you need to tell Rust what the return type there is. And Rust does have, like, some complicated things that so for so I think a good example of this is, now this this this introduces a lot of syntax at once, but I'm not gonna, like, explain everything. I'm just gonna say, like so let's say we have, like, I don't
37:28 know. That's an array. So what I'm doing is, like, I'm creating a I'm iterating over it, and I'm adding one to each element. And then what I'm doing is I'm saying, give me a new vector made out of this. And the thing is that this collect function can return a whole different set of types. Like, you can get a vector out of it. You can get a hash map out of it. You can get a b tree map out of it. You can get all kinds of things. So here, I have to say, like and doesn't matter really.
38:09 If to tell Rust that when you call collect, I want the vector out of it. And Rust will figure out what you're trying to ask for. And there are some cases so there are some cases where you're forced to do this, and you can also do Okay. And I'm assuming that's just because those types implement some sort of collect trait. Is that what makes that work? It's called from iterator. But yes. Exactly. And the collect function is generic over the from iterator type. Gotcha. I mean, I understand a little bit of that. So Yeah. Okay. So the the automatic
38:48 you know, if I the more Rust I write is, like, just don't bother with the type annotations. Let the compiler do it. I mean, the dev tools are pretty good too. Right? If I hover over numbers here Yeah. Or no. It doesn't seem to know what that is. Maybe because the code doesn't compile yet. I I don't know if it does. It does. Okay. No idea why that doesn't work then. Oh, it said Rust analyzer is currently indexing. Okay. That can happen. So if if you run the code with this, you'll see a panic, which is what
39:24 we're trying to do earlier. Yeah. Right. Okay. So whenever we use the unwrap function, we we have to be very confident as the developers that it's never Yes. Gonna have a non value. Okay. Yes. And yeah. Okay. Nice. Okay. Let's take a look at options too. Okay. So expected one off. Okay. I've seen this message before. Don't remember what it meant, but I've seen it before. And then we expected okay. So this looks like maybe it's a syntax header. It's mostly syntax, but there's more as far as I can tell. So let let's see which things you catch
40:11 first. Option two. Yep. There we go. I was closing those other windows to see if Rust analyzer would Oh, yeah. Catch up with me again. Don't wanna restart Versus Code because of our session, so I'll just leave it. Okay. Okay. So it's complaining here. Well, there's no edge statement. Well, yeah. Is that it? Kind of, but not really. Alright. Let's see what new error message is for You should look at the error and see what it says. It's That's that's that's still another error. We can fix that one later. We can fix this one first. Or, I mean, depends on what
40:57 you want. Yeah. I can do that. I'll I'll just text where I I mean, I'll do my best. Well, no. I mean, I've angered it. Okay. So cannot find value and scope. Yeah. We don't have a value. Yeah. So make this an let statement whose value is some type. Yes. So have you seen if let before? Yeah. But I I found it a little bit confusing. So, you know, let's just pretend I haven't. Yeah. So what's so what if let is is that so see, when when you say let x equal to, like, five, what you're saying is that
41:44 so x is actually a pattern, but I I don't think it's a good point to it's not a good idea to explain patterns fully yet. But Well, that's the same as Alexa. I think you said that as of Alexa. They must do something similar there. They say assignments aren't assignments. They're actually a pattern match. I don't know if that's the same here. So this is syntactically valid though, Rust. So this is this is a range. And you're saying that let the range from five to six be five. This makes no sense because if it this this doesn't this
42:14 doesn't make sense, but this is syntactically valid. What goes in the let is a pattern and what you're saying and, so you can do, like, let wild card equal to five, and Rust will be like, yes. This this five is a wild card. Like, good. And it will do nothing. So and and and and in patterns, you can put variable bindings, basically say that match it to this and set set whatever match to this variable. This is kind of like regex capture groups, but not really. And so, normally, this this pattern needs to be irrefutable. It needs to be such that
42:51 in a let statement, it needs to be such that whatever the input here needs to be able to match to this. And that's that's fine for x because any input will match to x and set to that value. That's fine for the wildcard because anything matches to wildcard. That's not fine for let some phi let some x equal to because if if if this oops. Alright. My keyboard shortcuts don't work, so I'm gonna if if if it were let sum x equal to none, this would not match. And then you've told you've told Rust to
43:30 say that this is the value of x, but it can't execute this because it won't. But this is where if let comes in place, where you can actually say so if you say if let some value equal to optional value, what you're saying is, alright. Try to match optional value against the pattern sum of value. If it succeeds, this branch will run, and, also, this branch will have the value from here. And if not, the else branch. So it's like it's an if with a little inbuilt match. Okay. That actually helps a lot. Okay. It's like is that it fixed? I'm assuming
44:19 we probably want the same Yes. Here. So if optional value is a none, it's just gonna run the second branch. Right? Yeah. Yes. If it's none and it's not none, but it will run the second second branch. And you can try that out if you want. But It's not quite happy yet. Let's see. Cannot find value lane 22. Oh, because I didn't type let. My bad. Alright. And it's saying option cannot be formatted on lane 23. So I wouldn't expect that to be an option there. K. So it's a vector of optional Yes. Eight bit integers.
45:36 So that's just the pop first. Right? And then does the comparison against the sum values. The same, it should be the the It's the same thing. Yeah. So it's gonna just yeah. But the option type is still what's been passed to print lane. Wait. What? That error does not makes oh. Oh, I see. So there's a fun fun thing here, which is that pop itself returns an option. Because if the new vector's empty, what's it gonna return? Nothing. So it returns an option of the type. So because we have a vector of options, when pop returns an option of an option.
46:23 Does that make sense? Yeah. So we can do that sum sum value. Oh. Did I not save it? Yeah. There we go. There. Yeah. And what it wants us to do here is it wants us to oh, and it it it it actually says that vector dot pop adds another layer of option t, and you can just stack option t's. And what it wants us to do is turn it into a while let. A while let is is like, so if let is the sort of pattern matchy version of if, while let is kind of the pattern matchy
47:03 version of while, which it does this instead of saying if a condition do something, it's as long as the condition is true, do the thing. So it's a while loop, but it's going to match on the it's gonna match on the value and the the results of this statement and give you a value each time. So what this will do is Oh, okay. Whole thing and return each value until it stops being some some value. Okay. That makes sense. So it's just gonna keep popping all those values off. Yep. Is the so is that something that does that mean
47:46 semantically, like, the actual con no. I don't know if you'd say concrete one, but the the pattern on the left and then the concrete value on the right, is that always the case? Can I swap those around? No. You cannot. They're the patterns these are syntactically two different things. So, like, on the left, you have a pattern. Okay. So you you can also put something like and and this will cause an infinite loop, but you can put a wildcard pattern there. But you can't put a wildcard pattern on the other side. Got it. Okay. Yeah.
48:13 Yeah. I think that's how Alexa works too, but I can't really remember off the top of my head. So that passes. Now I'm just gonna break it again and do none. And not break it, but that should print the value doesn't contain anything. Right? Yeah. Oh, because it doesn't know the type. And this is where you use the syntax. Well, it needs to be option of u eight. This is not gonna compile either. Yeah. This is exactly where the syntax comes useful. Not not that not that there's any point to this kind of code really existing
48:56 because you're creating a value and then checking what it is immediately. So but yeah. It's gonna say it doesn't contain anything. Yeah. I I guess the reason for typing this kind of code is educational. It's Exactly. Me understand it. Yeah. So in this case, Rust had no idea what the type was. Like, because it it you gave it none, so it didn't know what was in the option. It knew it was an option, but it didn't know what went in it, so it didn't have a complete type. And it didn't need to because it would never
49:29 hit the branch that needed to, but it it wants to know all the types completely. So you have to tell it what the type is. Okay. One more thing before we move on then. Just because what you're talking about there is the compiler trying to infer the thing. Can I remove the option here because it knows the x isn't in? Or because there's no none value. Right? So so you can't remove it. You can replace it with an a wildcard. So types can have wildcards too. I suspect this and, actually, in that case, because you
50:04 used VEC new here, so Rust already knows that this is a VEC of something. It doesn't know what that something is. I suspect it won't compile. There's no reason why Rust won't like, Rust could make this compile at some point. My suspicion is that right now this won't compile. And this is basically that. Or okay. It does. So it's it's smart enough to be like, it's a Vec, and it's it it's like, I'll calm down. I won't I won't I know you haven't told me what the whole of Vec is, but I'll wait a little bit and see if you can tell
50:38 me eventually. And so it's like, right now, all I know is that it's a VEK of something. And, oh, okay. You pushed something to it. Now I know what it is. But there there will be some cases that are similar to this where it won't. So yeah. What what I what I always do is just, like, write it without the types. And then if it complains about types that I know what they are, I just add them in. Alright. Clever compiler. Okay. So that was options. Let's you wanna jump over to enums then? Sure. Yeah. Alright.
51:00 Enums
51:10 Let's run the test. Enums one. Okay. What have I got here? So no variant or associated item named quit. Fan for enum message lane 12. There's a nice big fat to do saying, well Yeah. So that's just an empty enum. Right? It's it's actually got no definitions types. Okay. I mean Yeah. We're assuming this is gonna work? That is gonna work. Yeah. Are the commas optional, mandatory? Mandatory. Okay. Yeah. Commas are optional in a few cases, but not for enums. The last one is optional, but yeah. Yeah. I think if if they're mandatory anywhere, I'll probably just put it on the last
52:06 one too. Alright. That works. So this syntax is is that how we always let's see if I can get auto complete. Yeah. Okay. That's just how we reference members of a a new node. Yeah. Basically so have you seen, like, pads and modules yet? Like, have you seen the use statement? Yes. I have. So, like, this is this is just a variant of that. And what's what's going on here is that the enum message kind of has children, and they're scoped under message. So you can also do, like, use if you did use message quit, then you could just say quit.
52:49 You could just say quit. And that's what happens with option, by the way. Because if you notice, we didn't have to do option some and option none, and that's because by default, Rust directly imports none and sum. Alright. Cool. Yeah. Can you know, I've used the syntax and never really known what it does. Can you maybe explain that to me too? Oh, sure. So the print this is the this is the print format string the format string syntax in Rust is pretty robust. And oh, well, that didn't autocomplete. So, like, when when you don't when you
53:28 use this, it means that this is this parameter. And you can also, like, number parameters. You can even name them, like, and you can say, equal to five. You can do all kinds of stuff here. But when when you're just using braces with no question mark, this means use what's called the display implementation. So there's a trait called display, and it's it's implemented for types that have that are intended to be displayed to the to to the end user, and the implementation tells you how you do that display. This thing means debug, and what it means is that
54:09 this output might be ugly, and you probably don't want to actually display it to the user. In this case, actually, what the user would see is just the words quit, echo, move, or change color because so this this custom this this derive is automatically deriving an implementation. But, for example, if you did oh, if you did derive on debug and then destruct foo, the the debug implementation would print some something like which you probably don't wanna show users. Yeah. So display is and, typically, display is something that is written manually. You will you will you will look at
54:58 your type, and you will decide how best it is to how best to display it to the user, and you'll write a manual implementation, whereas debug is almost always custom derived that way. So this is basically while debugging, if you use the colon question marks and tags, you can just this is how you'll see your own types, and it will show you basically everything that's in them. Okay. So I I just wanna confirm I understood that correctly. If I had to move that's arrived debug in this, will this fail to compile? Nope. Yeah. Cool. Yes. Alright. That makes a lot of
55:32 sense. You know, all you need is someone just to explain it and, like, boom. Like, cool. Alright. So it's very it's very normal to just slap derived debug on basically everything you write, every structured enum in your program because at some point or the other, you're gonna print it out for debugging. Cool. Good to know. Alright. Let's pop open enums two. Let's see. So we don't have any variants for our message one, but I can see here we do oh, these ones are getting fancy. Okay. Oh, we got a plain move and a plain quit.
56:09 We don't have a plain move, but we'll get to that. Oh, yeah. Let's do the other two because you've seen the syntax there before. Okay. So we got oh, well, let me start from this. So we got change color. Alright. So change color is a triple. Right? So that means I did that. Yep. I echo triple of string. Yep. Yeah. It's a stellar triple of this one, I guess, so maybe I think it It's just we just most enum variants look like this, so we don't actually they're called technically called tuple variants, but we just call them variants
57:00 or data full variants because almost always, they look like this. Almost always, they use the parentheses and not the braces. The braces are actually pretty rare, and you you use them when like, over here, it's pretty obvious that this is red, green, and blue. But if you had, like, a bunch of u 30 twos and they all were different things that, like, were different concepts, it is often better to just name them. And that's where the thing used for move comes in, where you actually have to give them a name. And that that you just do, but, like,
57:32 it it uses the struct syntax like that. Nice. So enums can really be a pretty flexible component then. Yes. And I can I mean, one of the things I'm thinking of in my head as soon as I've seen this is, like, you know, if I wanna write endpoints that take gRPC messages, like, that could just be an email of all the different messages I can receive on an endpoint, and I could match and work on that or something? Exactly. Yes. And match is how you usually deal with enums. Like, I mean, I I showed the match
58:09 with just values earlier, and I'm sure that probably the next exercise will have this. But I showed the match with values earlier, but the match statement takes patterns. So you can do things like match match message and say, move x y something, quit something, echo something. Ah, cool. I like it. Alright. Let's see if we fixed this then. Items too. We did. Okay. What's this call function? Oh, no. It's just the ah, okay. And all it does is it uses the debug implementation. And I guess if you run it, you'll realize that the debug implementation of this enum
58:47 is not very pretty. It's not something you'll wanna show to the user. This one? Yeah. This is this like, this is not something like, if if if you had a user, end user, not a programmer using your application, you probably wouldn't wanna show them change color 200, two 50 five, two 50 five. You'd wanna show them, like, change the color to this color or something. So Yeah. Okay. Also, this is the this is probably an internal message, so you'd never show it to the user in the first place. But yeah. Oh, you'd hope so. But, you know, I'm
59:25 a bit of a caretaker all of this stuff. So I'm just gonna make them smaller and make this bigger. Is that gonna does the compiler know? The compiler does know. I'm your compiler. Cool. And is there I mean, U S R A 2 is not exactly massive. Right? I mean, is there only special circumstances you would choose anything smaller? Like, is there any rules around that? There's nothing smaller than u eight. I mean, in this case, if you're talking about two fifty six bit color, you probably wanna use a u eight so people don't give you a number that's too
59:56 big. Yeah. Like, that that's basically when you start using u eights. And often, you actually want to size optimize. Often, like I mean, I've worked on browsers and stuff, and in browsers, you often have these trucks that are just everywhere. Like, for example, the node type in JavaScript, like, doc for elements, everything is a node, you need to allocate that again and again. So if you if you can just shave off, like if you can pack a bunch of things that don't need to be 32 bit integers into eight bit integers, then you can just
1:00:31 have more of them, and you don't end up allocating so much because you're allocating them thousands and thousands of times. Cool. That's good to know. Alright. Let's see what this last one is, and then we'll decide what we wanna finish on. So in a message, a struck point oh, yeah. I'm gonna need a another message here. So let's see. Oh, I see. So variant or associated item change color is not found here, and this is on Lane 54. Yeah. So this is the state process. Oh, so I'm assuming this process maybe doesn't match? Yes. Except we have to write it. Ah,
1:01:26 there we go. This will create a match expression to process the different message variants. Sorry. The message. What does it pass in? Just a straight up type. So that's just gonna be, like, this. Right? Yeah. Well, not quite. But we should first define the enum because that's also not done. Yeah. Okay. Alright. What do we got then? Let's just copy these. You also won't want the message colon colon. Yeah. Yeah. So we got our quick message. We've got our change color, which actually needs to be So there's a subtle difference between this one and the last one. And if you compile
1:02:46 it, you might notice. What was that? Just double brackets. So it's actually expecting a triple of Yeah. It's what it's decided what they've decided is that the color type that they're using is a triple itself. Yeah. Okay. This one just has to be string and this one just needs to know. Oh, well, this is a type. Right? So can I just do This is just like the string one? So now this is they've made it a little bit clearer, cleaner, and so now there's just a point type. So the Rust compiler doesn't care. The structure
1:03:20 is defined after the enum, none of the ordering matters. Okay. Cool. Within the same crate, everything can find everything. I mean, if if you create something recursive that it doesn't like, it might cause a problem. But you you can you can have you can have self referential stuff as well. Okay. State looks fine. So now we're back to this match statement then. Yeah. So we're matching on message, and we wanna alright. What was this? Quiet was just empty, wasn't it? Yep. So well, we want okay. We can just do an empty for now. Yeah. I thought
1:03:58 maybe if I could just get it to compile. Yeah. That's a good point. The test failed, so it compiled. That's good. Yeah. So what's it actually expecting? It wants me to set the properties on the state to be the values. Right? Yeah. But the but the state function already has methods that do this. So I think we just need to call them. Ah, okay. So for quit, we're just calling quit? Self dot quit. But yeah. And then we wanna handle message, change color, message. Right? Nope. Remember what we did in the if let to get access to the thing inside the
1:04:52 enum? Oh, yeah. So we need to do that? Yep. K. Yeah. Also, just to note because so remember when we when the color was not a tuple? If if it was like this, you'd do something like this, if that makes sense. Oh, yeah. Yeah. Okay. Yeah. Because this tuple is encapsulated. Just a single this is just a single thing. Yep. Okay. And we've got one more, don't we? So Echo. Echo. It also takes a param. So See. Neat. Cool. And I'm assuming removing this, the compiler is gonna complain that we don't handle all the cases. Exactly.
1:05:52 Yep. We we could just do that, but then the test will fail. So No. The test will actually pass because the echo thing doesn't do anything that can be tested. Alright. We'll leave it like that. Alright. Let's pick one more. Yeah. Anything you think would be fun to do? I mean I I mean, what what do you what would you like to do? Okay. So there's a couple of things. I think let's pick the one that you're most interested in. Now I think maybe doing something around testing would be cool. Clippy's jumping out at me because I don't know what Clippy
1:06:00 Clippy
1:06:31 is. Yeah. Maybe one of those two. So Clippy is Clippy is Rust's linting tool, and the Rust compiler tells you when your code's wrong. Clippy tells you when your code's bad or not perfect. Okay. Yeah. Alright. Yeah. Well, I guess we could take a really quick look at Clippy, and then maybe we'll take a quick look at tests, we'll that should take us up to our time. So Yeah. So Clippy is a tool is a collection of lens to analyze your code. Okay. Let's run Clippy one and see. Is it I saw it just run Rustling Stone.
1:07:13 I'm not actually sure how they do the thing. But ah, okay. Yeah. They've they've set it up so that it fails to compile when Clippy has an issue. So the Clippy Clippy warnings show up as Rust warnings when you use Clippy. So they look the same. Okay. And we've got a whole bunch okay. So this is, like, one of these Lent rules is on by default, which means that it's telling us our code could be better. Yeah. And this is telling us that we're comparing a 32 bit float with a 64 bit float, which is maybe bad. Oh, no.
1:07:45 We're just comparing floats. It like, we're comparing floats for equality. And the problem with comparing floats for equality is, for example, things like one three divided by two point is not actually 1.5 because floats are weird. Yeah. This is the weird precision thing. I think every language has that. Right? Everything. Yeah. Everything with I I three floats has has that. So so the the idiomatic way of doing float comparison where you're comparing for equality, like, x greater than y is fine, but x equal to y or not equal to y is you you what you do is you compare them
1:08:29 up to, like, a small delta, like, a small error value. And what people often do is so if you look at the error message, it actually tells you what to do. Use absolute I'll just copy this. So if and we need to pick pick an error value. So you could just put point zero zero one there. It's a pretty big number, but you like, yep. What did you tell me to put that? Point zero Some some small number. Probably smaller than that, actually. Like, just a bunch of zeros. The the clip, yeah, actually goes on something
1:09:19 else. Yeah. You're you're using, like, a very small error value. You're saying that I'm I I consider these to be equal if they're close if they're closer than that number. Yeah. I understand that. Yes. So we're doing y minus x. Absolute. And then checking to see if the differential is bigger than the small number that we're willing to accept as an error margin. Okay. Yeah. Cool. And the message probably would have told me that. I'm not sure. Said there's an f 32 epsilon. So the Rust standard library you has like, you can also what you can put
1:09:51 here, and I think you might have to import it. But if you do this or f 64 because we're using f 60 fours. If you do this, it will that is the value the Rust standard library recommends. Yeah. That worked. Compiled and And the Epsilon is just Rust standard library accepted marginal value, I guess. Yeah. Actually Does it define it if I hover? Or not because my analyzers are not okay. This is this is the difference between one and the next largest representable number. So this is actually kind of, like, the smallest difference. Often, you will want something, like, bigger.
1:10:41 It like, epsilon's very small. So it depends on what you want. Like, if it's if, like, if you really care about that much precision and your numbers are that precise, you would use Epsilon. But in this case, maybe not. Like, in this case, you just figure out where you want things to be the same. Alright. Sweet. Alright. What does Clippy two tell me then? Let's just run it. Also, if you're, like, on your own projects to run Clippy, you just run cargo Clippy. Oh, yeah. I I don't know how this project is set up, I don't wanna run
1:11:15 that against everything here. But This is this project is set up differently. So this is complaining because it says for loop over an option, which is an option. This is more readable. Okay. So this is telling me that my code is just more readable if I change it to an outlet. Yeah. Like like I said, it doesn't tell you your code is wrong. It tells you your code is bad. Alright. Okay. Used to its warnings then, I guess. Yeah. And you can also not it's not that big a deal to not use Clippy. Some people it it often teaches you things, so
1:11:49 people like to use it as a learning tool. But so what it's saying here is that so you know how you can do, like, four x in vector? So vector is an iterable, which means you can give it to a for loop or iterator functions. So it turns out option is also an iterable, and it it's an iterable that either if it's some, it gives you that one value. If it's none, it gives you nothing. Just like a vector is an iterable that gives you all the values it has. So sometimes this sometimes causes confusion.
1:12:21 You you rarely want to say iterate over everything in this option because that's just an if let. So what Clippy is telling you to do is just use an if let here because this is confusing because people don't usually remember that an option isn't iterable. It's like, there there are good reasons for option to be an iterable, but it also leads to sharp edges like this where you have a four loop and you're like, oh, this is looping over many objects. And it turns out it's only looping over the option, so it's only looping over one.
1:12:41 Skipping Primitive Types
1:12:52 Yeah. That makes sense. Okay. And if yeah. Okay. That makes sense. Let's yeah. We don't need to save that one. Let's go for let's trace tests. So, I mean, we've already kinda seen this syntax a few times now because some of the rustling exercises actually ship with a test. So are assertions always macros? Yeah. Assertions are macros because they're macros because they actually kind of they the like, if you if you say assert, like, one equal to two, it will actually print this part out along with the assertion. And I think you can use you can even use format string syntax
1:13:02 Introduction to Move Semantics (Part 1: Mutability)
1:13:41 if it fails. Like actually, I'm not sure. I don't think I don't think you can, but it it will actually print this out. So they're they're macros so that that can look good. Okay. Otherwise, you'll just get assertion failed and then, like, a string if you added one. But if you yeah. Over here I'm just asserting that the statement evaluates to true instead of brackets. That's Yeah. That's it. Okay. Run test one. Yep. And if if if it were if it one one equal equal to two, you'll see that it'll say assertion failed one equal equal to two. So it needs
1:14:19 to be a macro so it can actually say that one equal equal to two because if it was just a function, the function has no idea what expression you put in its parameters. It only knows the value of that expression. So that that's that's why it's a macro. Alright. Cool. And what is this CFG test? So so in in in Rust, basically, this is this is saying only do this in test mode. Only compile this module in test mode. So right now, it doesn't really matter. If you just did if you just took this function and put
1:14:31 Understanding Move Semantics & The Copy Trait
1:14:57 it outside, it wouldn't matter. But often, sometimes you'll have, like, imports and stuff that you're using in your tests. It is standard practice to put all of your unit tests in, like, modules that are called test or tests, and you put a CFG test on it. So the normal normal compilers don't even see that code. And, when it's in test mode to see if, their compilers invoked with CFG test on, And now then suddenly this module is enabled, and then it finds the test. It's it it prevents it prevents like, if you're writing a library because, again, like, these are you
1:15:13 Explaining Move Semantics & Cloning
1:15:35 use this is this is how you'd write a unit test, typically. And if your library has lots of unit tests, you don't want downstream crates to care about them, and you don't you don't want, like, your imports to be mixed in with the other imports. For example, in your test, you might be importing some test dependencies, and you want to be able to import them without the rest of the code having to also import it. So you use CFG and you do some cargo feature stuff and a bunch you use a bunch of things. But yeah.
1:16:04 Okay. And that just tells that that that's a test that should be run to evaluate the a a service or not? Does that mean I can have functions inside of this module block that don't get run as tests? Yep. Only things that are pound test are evaluated as tests. Okay. And you said that unit tests should be in this format. So, like, integration, acceptance test, they all live in a test folder then, I guess. Yes. There's a test folder, and you still write tests with pound test, but you won't use CFG test because that folder will always be invoked with CFG
1:16:38 test. Okay. Gotcha. Cool. Alright. Let's pop over one more. So are these just introducing new macros? Okay. That was a bit different. Okay. So this isn't a set equals? I mean, I feel like we just didn't necessarily calls. Right? Oh, is that because alright. Okay. That's gonna accept this syntax. Right? Yep. And and we we actually want to fix this so that assert can just do this. But what the reason it's a separate macro is so that if it's not equal, it can actually just tell you these are the two values. Instead of just saying assertion failed, it'll say
1:17:21 assertion failed because one is not equal to two. Okay. I guess that's not really a problem for simple cases where it's just two numbers. But for maybe you're comparing two objects to structs, something like that, it may be more important for those Yes. For two numbers. Separately. It often it actually often matters for numbers. Like, you might do assert equal vec dot len and zero to make sure that it's zero. There's you can also assert that it's empty. But if it's not zero, you might wanna know, like, does it have one thing? Does it have
1:17:52 500? So, like, you often do it that. Even like, I've I've used this for integers. Okay. I'm assuming there's other. Yeah. There we go. Those are not equals. So Yep. Also, there's, like, I think, a proxy. There's there's a bunch. I lost the message just now, so I'll stick with that. That's on test two. It's happy. Just find out. Yeah. Yeah. Because because they're two different parameters, it can actually separate them on this view. Yes. Okay. Alright. Let's see what this last one is, and then we can finish up there. So we have a function doing modules even,
1:18:39 returns a bill, and we have to just write the test to make sure the function works. K. So if I call as even before, we would expect that to be true. Yep. You don't actually have to have the equal equal to true. Alright. Okay. Yeah. Of course. Yeah. Because the the statement within the macro has to evaluate. Okay. Well, you'd have to And this one does. Right? Or You can just put a not there. Some people actually do prefer the equal equal to false over the not just because you it's easy to forget the not. It's
1:19:31 easy to miss it while reading. But yeah. Yeah. Yeah. I've always been taught to try and, like, I had this drummed into me from very early days. They always put the static value on the left so it's easier to read. I don't know if that's true, but Yeah. That's true. Those are called Yoda conditions. Yoda conditions. Yeah. Yeah. I just always had that in green with me and this kind of stuck. So I'll just I'll just copy that too. Alright. I've got one more test to do. So alright. I already did that. Oh. Well no. Wait.
1:20:17 Is false then odd? Left is one. Right is two. Oh, it's it's failing on test two. It's failing on the other test. You're running the wrong test. Oh. Sorry. Oh, you're running the wrong file. Yeah. Yeah. Each file has multiple tests. That was confusing. But I don't I don't like when I look at trivial code and I can't work at what's going on. So that's good. Alright. Awesome. Thank you for that. That was really good fun. Yeah. So that was we got through quite a lot there. I think that was a really good exploration of pretty much everything that was
1:20:55 left in these exercises. There's only a few things there. So I just wanna say thank you for, you know, taking the time out of your day. I know you must have a lot on your plate. You're starting a new role next week. So, you know, thank you for making the time here and and kinda guiding me through this and sharing your knowledge. It was it was really really really good. I enjoyed it. Yeah. You're welcome. This is really fun. Thank you. Well, you have a great rest of your day. Enjoy your weekend. Good luck with your new job at Google next week
1:21:22 and thank you again. Yeah. Cheers. Thank you for having me. It was great. Cheers.
Technologies featured
Meet the Cast
Stay ahead in cloud native
Tutorials, deep dives, and curated events. No fluff.
Comments