-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Guide: if #15276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Guide: if #15276
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -611,6 +611,153 @@ concept: `if`. | |
|
||
## If | ||
|
||
Rust's take on `if` is not particularly complex, but it's much more like the | ||
`if` you'll find in a dynamically typed language than in a more traditional | ||
systems language. So let's talk about it, to make sure you grasp the nuances. | ||
|
||
`if` is a specific form of a more general concept, the 'branch.' The name comes | ||
from a branch in a tree: a decision point, where depending on a choice, | ||
multiple paths can be taken. | ||
|
||
In the case of `if`, there is one choice that leads down two paths: | ||
|
||
```rust | ||
let x = 5i; | ||
|
||
if x == 5i { | ||
println!("x is five!"); | ||
} | ||
``` | ||
|
||
If we changed the value of `x` to something else, this line would not print. | ||
More specifically, if the expression after the `if` evaluates to `true`, then | ||
the block is executed. If it's `false`, then it is not. | ||
|
||
If you want something to happen in the `false` case, use an `else`: | ||
|
||
``` | ||
let x = 5i; | ||
|
||
if x == 5i { | ||
println!("x is five!"); | ||
} else { | ||
println!("x is not five :("); | ||
} | ||
``` | ||
|
||
This is all pretty standard. However, you can also do this: | ||
|
||
|
||
``` | ||
let x = 5i; | ||
|
||
let y = if x == 5i { | ||
10i | ||
} else { | ||
15i | ||
}; | ||
``` | ||
|
||
Which we can (and probably should) write like this: | ||
|
||
``` | ||
let x = 5i; | ||
|
||
let y = if x == 5i { 10i } else { 15i }; | ||
``` | ||
|
||
This reveals two interesting things about Rust: it is an expression-based | ||
language, and semicolons are different than in other 'curly brace and | ||
semicolon'-based languages. These two things are related. | ||
|
||
### Expressions vs. Statements | ||
|
||
Rust is primarily an expression based language. There are only two kinds of | ||
statements, and everything else is an expression. | ||
|
||
So what's the difference? Expressions return a value, and statements do not. | ||
In many languages, `if` is a statement, and therefore, `let x = if ...` would | ||
make no sense. But in Rust, `if` is an expression, which means that it returns | ||
a value. We can then use this value to initialize the binding. | ||
|
||
Speaking of which, bindings are a kind of the first of Rust's two statements. | ||
The proper name is a **declaration statement**. So far, `let` is the only kind | ||
of declaration statement we've seen. Let's talk about that some more. | ||
|
||
In some languages, variable bindings can be written as expressions, not just | ||
statements. Like Ruby: | ||
|
||
```{ruby} | ||
x = y = 5 | ||
``` | ||
|
||
In Rust, however, using `let` to introduce a binding is _not_ an expression. The | ||
following will produce a compile-time error: | ||
|
||
```{ignore} | ||
let x = (let y = 5i); // found `let` in ident position | ||
``` | ||
|
||
The compiler is telling us here that it was expecting to see the beginning of | ||
an expression, and a `let` can only begin a statement, not an expression. | ||
|
||
However, re-assigning to a mutable binding is an expression: | ||
|
||
```{rust} | ||
let mut x = 0i; | ||
let y = x = 5i; | ||
``` | ||
|
||
In this case, we have an assignment expression (`x = 5`) whose value is | ||
being used as part of a `let` declaration statement (`let y = ...`). | ||
|
||
The second kind of statement in Rust is the **expression statement**. Its | ||
purpose is to turn any expression into a statement. In practical terms, Rust's | ||
grammar expects statements to follow other statements. This means that you use | ||
semicolons to separate expressions from each other. This means that Rust | ||
looks a lot like most other languages that require you to use semicolons | ||
at the end of every line, and you will see semicolons at the end of almost | ||
every line of Rust code you see. | ||
|
||
What is this exception that makes us say 'almost?' You saw it already, in this | ||
code: | ||
|
||
``` | ||
let x = 5i; | ||
|
||
let y: int = if x == 5i { 10i } else { 15i }; | ||
``` | ||
|
||
Note that I've added the type annotation to `y`, to specify explicitly that I | ||
want `y` to be an integer. | ||
|
||
This is not the same as this, which won't compile: | ||
|
||
```{ignore} | ||
let x = 5i; | ||
|
||
let y: int = if x == 5 { 10i; } else { 15i; }; | ||
``` | ||
|
||
Note the semicolons after the 10 and 15. Rust will give us the following error: | ||
|
||
```{ignore,notrust} | ||
error: mismatched types: expected `int` but found `()` (expected int but found ()) | ||
``` | ||
|
||
We expected an integer, but we got `()`. `()` is pronounced 'unit', and is a | ||
special type in Rust's type system. `()` is different than `null` in other | ||
languages, because `()` is distinct from other types. For example, in C, `null` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. C has There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sometimes, people call That said, you're right, I can probably expand this a bit. I didn't explicitly say that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
is a valid value for a variable of type `int`. In Rust, `()` is _not_ a valid | ||
value for a variable of type `int`. It's only a valid value for variables of | ||
the type `()`, which aren't very useful. Remember how we said statements don't | ||
return a value? Well, that's the purpose of unit in this case. The semicolon | ||
turns any expression into a statement by throwing away its value and returning | ||
unit instead. | ||
|
||
There's one more time in which you won't see a semicolon at the end of a line | ||
of Rust code. For that, we'll need our next concept: functions. | ||
|
||
## Functions | ||
|
||
return | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to imply that this line sets both
y
andx
to5i
, whereas in reality (because assignments return()
) it setsy
to()
andx
to5i
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, clarifying that was part of the intent of my feedback here: #15276 (comment) but I have at this point assumed that @steveklabnik is deliberately trying to avoid discussing this distinction at this point in the tutorial.