From 08e8336d05597c8af6f2741cede8d45002e0495e Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 30 Jun 2014 12:17:52 -0400 Subject: [PATCH] Guide: if --- src/doc/guide.md | 147 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/src/doc/guide.md b/src/doc/guide.md index 0fc9671c4db29..024bb8ce89bf8 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -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` +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