The Rust Programming Language Booleans, Conditionals, Statements, and Expressions
Transcript from the "Booleans, Conditionals, Statements, and Expressions" Lesson
>> And finally, let's talk about Booleans. So Booleans, there's two of them, you got your true, and you got your false, those are the only two Booleans. And so here we defined, should we go fast to be true, and should we go slow to be false. If you want you can use as to convert between a Boolean and u8, because behind the scenes Boolean is just a u8 that's kinda the smallest thing that hardware will let you do.
[00:00:24] Theoretically, you can represent a Boolean as just a bit, but there isn't any hardware that I'm aware of, that actually has precision that's small, usually the smallest storage size they have is a byte. You could do something fancier if you wanna actually only take up a bit. So true will evaluate to 1, and false will value to 0, because that's basically how it stores them.
[00:00:42] True is 1 and false is 0 at runtime. There's also double equals in Rust, which is a way to compare things. Of note, double equals is what's known as structural equality, which is to say that if I were to put two collections here and compare that with double equals, Rust would actually go through normally and actually compare all the contents of both of those collections.
[00:01:05] It wouldn't be reference equality you might be used to in some languages. Also worth noting that this double equals is really the only equals you've got in Rust, there is no triple equals. So always use double equals, because that's the only one there is and it's probably gonna do what you want.
[00:01:22] Okay, let's talk about conditionals briefly because these use Booleans. So here we have an if conditional, so I say if cats greater than 1, println multiple cats. We can also throw an else on there, and say need more cats. That's totally valid. Of note, compared to a lot of languages with similar syntax in Rust, you do not need parentheses around this, you can leave them off.
[00:01:43] But, you do always need the curly braces. So I know in a lot of languages you can say, if parentheses and then leave off the curly braces, Rust is not having that, in Rust, you always have to have the curly braces, which I think is a good decision personally.
[00:01:57] Also of note, this condition right here, this cats greater than 1, this thing has to be a Boolean. There's no truthiness in Rust, this can't be a number, it can't be a string, it can't be anything. If this is not exactly a Boolean value, you'll get an error from the compiler.
[00:02:10] Fortunately, using double equals you can always pretty easily get something into a Boolean if that's what you want. You can also do else if, so, else if in Rust is basically exactly the same as it is a lot of languages. And, what to take a brief detour here and talk about statements and expressions.
[00:02:30] So an expression in Rust, is something that evaluates to a value. So for example, here's an expression, cats is greater than 1000. That's an expression, because that will evaluate to either true, or false. That's the value. This also breaks down into sub expressions, like cats itself is an expression, and 1000 itself is an expression.
[00:02:49] Each of those they evaluate to a value, so they are expressions. So both of them individually are expressions, and also the entire thing with them put together is an expression. Similarly, with a function call, we can say okay, this is an expression, this is an expression, this whole function call is an expression, and the entire thing there is an expression.
[00:03:08] All of these are expressions, they're things that evaluate to a value. A statement on the other hand, does not evaluate to a value. So, the canonical example of this we've been using are our calls to println, this does not evaluate to any particularly useful value, so there's a semicolon at the end, which kinda is a good clue that.
[00:03:26] So, a statement, generally speaking will end with a semicolon. It's not always true, like conditionals, like if can be used the statement and you don't have to put a semicolon at the end. But, anytime you do see a semicolon, you're definitely dealing with a statement. Okay, why does this distinction matter?
[00:03:42] Well, it matters because in a number of cases, the Rust compiler sorta gives you these little conveniences, when you choose an expression over a statement. So here's an example, we had this function earlier, multiply both x and y, both taking f64, returning an f64. Then we said return x times y.
[00:04:23] So you don't need to write out return, you don't need to use the return keyword, instead, you can just say, x times y, and that's it. Both of these functions are exactly the same. There's no performance trade off here, this is just something that the Rust compiler does for you.
[00:04:38] So this is the rule, if a function ends with an expression rather than a statement, it automatically returns that expression. But of note, you don't wanna put a semicolon here. If you put a semicolon, then this becomes a statement, and then Rust says, Hey, I thought you were gonna return something, how come you didn't return an f64 like I thought you were gonna?
[00:04:56] So basically, either say return and semi colon, or leave off both the return and the semi colon, and either of those are gonna be equivalent. Now I mentioned this in the context of ifs, because this is also a thing that you can do with ifs. It's not just functions.
[00:05:09] So actually, each of these if branches, follows the same rule as functions do, which is to say, if you want to, you can leave off the semicolon here and have the entire branch, evaluate to whatever the expression is here. So here's an example of that. I can say let message equals, and then use if basically like a ternary, if you're familiar with those, where I'm saying, okay, if cats is greater than 1, then message will be equal to this expression right here, multiple cats.
[00:05:34] Cats greater thousand, then message will get set to this expression right here, and otherwise message will get set to need more cats. So here, although each of these branches does not have a semicolon, this is an example where I do need a semicolon at the end there, which is sorta going with the let, because lets are statements.
[00:05:53] I'm gonna highlight this semicolon little bit, because yeah, I definitely forget this all the time whenever I'm doing this like, let equals if. And the compiler error messages uncharacteristically unhelpful. So, if you're gonna use let equals and if, definitely don't forget that semicolon. Any questions about statements and expressions, and how you can use them for automatic return and functions, or in if branches like this.
>> The last expression would be the return then, it sounds like.
>> Right, yeah. So if I wanted, I could put other stuff in this block above this. And that would be the same thing as it would be the function where the last expression in there would automatically become the result of the whole branch.
>> If a function contains more than one expression, then also can we still do this?
>> Great question. Yeah, if a function contains more than one expression, also can you do this. So basically the rule is, you can have as many statements as you want in a row, and then after that one final expression, but if you tried to put like, here's an expression, then here's another expression, you get a compiler error.
[00:06:52] So it has to be a bunch of statements followed by an expression at the end that's just sorta hanging out, and that last expression right there, that's sorta the last thing in the function. That's what gets turned to the automatic return, and it's the same thing inside these if blocks.
>> Would you wanna specify explicitly every return statement if you're trying to make an early return?
>> Yeah, for sure. Yeah if you wanna early return, then you do need to use the return keyword for that. But if you want, you can can do an early return, and then still at the end have the default return use the expression style, that's totally fine.
[00:07:24] But yeah, if you wanna do an early return, either you need to do that, or you need to do something like this where you have conditionals throughout the function and, it's always possible to, you never need to use the return keyword, you can always express the same thing without it.
[00:07:38] But yeah, early returns generally use the return keyword for that.
>> For the comparison, I'm guessing that it takes that type into account like a string of 1.1. What it would be false if it was compared to a float of 1.1?
>> That's even better you would get a type error.
[00:07:56] So if you tried to compare, if you said the string foo is greater than 1, it would just not compile.
>> And then the second question was kinda setting these variables of conditional statements. Can you then pass around in the string interpolation?
>> Sure. Yeah, if you want to.
>> Yeah, that's awesome.
>> Yeah, so actually this entire if like starting from the i and f to this close curly brace, that whole thing is an expression. So you can just pick that up and drop it anywhere you want. [LAUGH]
>> Can that be used for knowledge coercion?
>> Great question. Can that be used for knowledge coercion? Rust actually does not have a concept of null, or undefined, or any similar kinds of coercion. We'll see how the alternative that it has when we get to Enums. But basically, those are no nil undefined, anything like that, Rust does not have it.