Skip to content

Commit

Permalink
WIP control-flow-basics
Browse files Browse the repository at this point in the history
  • Loading branch information
djmitche committed Sep 7, 2023
1 parent 72c52d5 commit 0592424
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 376 deletions.
4 changes: 3 additions & 1 deletion src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
- [Arithmetic](hello-world/arithmetic.md)
- [Exercise: Hello, World](hello-world/exercise.md)
- [Control Flow Basics](control-flow-basics.md)
- [Control Flow](control-flow-basics/control-flow.md)
- [Conditionals](control-flow-basics/conditionals.md)
- [Loops](control-flow-basics/loops.md)
- [`break` and `continue`](control-flow-basics/break-continue.md)
- [Blocks and Scopes](control-flow-basics/blocks-and-scopes.md)
- [Functions](control-flow-basics/functions.md)
- [println! and dbg!](control-flow-basics/println-and-dbg.md)
Expand Down
4 changes: 3 additions & 1 deletion src/control-flow-basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

In this segment:

* [Control Flow](control-flow-basics/control-flow.md)
* [Conditionals](control-flow-basics/conditionals.md)
* [Loops](control-flow-basics/loops.md)
* [`break` and `continue`](control-flow-basics/break-continue.md)
* [Blocks and Scopes](control-flow-basics/blocks-and-scopes.md)
* [Functions](control-flow-basics/functions.md)
* [println! and dbg!](control-flow-basics/println-and-dbg.md)
Expand Down
83 changes: 23 additions & 60 deletions src/control-flow-basics/blocks-and-scopes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,20 @@
minutes: 10
---

<!-- NOTES:
Mutable and immutable variables, scopes, shadowing, block values, expression values (e.g., value of an if expression)
-->
# Blocks and Scopes

# Scopes and Shadowing

You can shadow variables, both those from outer scopes and variables from the
same scope:

```rust,editable
fn main() {
let a = 10;
println!("before: {a}");
{
let a = "hello";
println!("inner scope: {a}");
let a = true;
println!("shadowed in inner scope: {a}");
}
println!("after: {a}");
}
```

<details>

* Definition: Shadowing is different from mutation, because after shadowing both variable's memory locations exist at the same time. Both are available under the same name, depending where you use it in the code.
* A shadowing variable can have a different type.
* Shadowing looks obscure at first, but is convenient for holding on to values after `.unwrap()`.
* The following code demonstrates why the compiler can't simply reuse memory locations when shadowing an immutable variable in a scope, even if the type does not change.

```rust,editable
fn main() {
let a = 1;
let b = &a;
let a = a + 1;
println!("{a} {b}");
}
```

</details>
# Blocks
## Blocks

A block in Rust contains a sequence of expressions.
Each block has a value and a type,
which are those of the last expression of the block:

```rust,editable
fn main() {
let z = 13;
let x = {
let y = 10;
println!("y: {y}");
let z = {
let w = {
3 + 4
};
println!("w: {w}");
y * w
};
println!("z: {z}");
z - y
};
println!("x: {x}");
Expand All @@ -73,23 +24,35 @@ fn main() {

If the last expression ends with `;`, then the resulting value and type is `()`.

The same rule is used for functions: the value of the function body is the
return value:
## Scopes and Shadowing

```rust,editable
fn double(x: i32) -> i32 {
x + x
}
A variable's scope is limited to the enclosing block.

You can shadow variables, both those from outer scopes and variables from the
same scope:

```rust,editable
fn main() {
println!("doubled: {}", double(7));
let a = 10;
println!("before: {a}");
{
let a = "hello";
println!("inner scope: {a}");
let a = true;
println!("shadowed in inner scope: {a}");
}
println!("after: {a}");
}
```

<details>

Key Points:
* The point of this slide is to show that blocks have a type and value in Rust.
* You can show how the value of the block changes by changing the last line in the block. For instance, adding/removing a semicolon or using a `return`.
* Show that a variable's scope is limited by adding a b` in the inner block in the last example, and then trying to access it outside that block.
* Shadowing is different from mutation, because after shadowing both variable's memory locations exist at the same time. Both are available under the same name, depending where you use it in the code.
* A shadowing variable can have a different type.
* Shadowing looks obscure at first, but is convenient for holding on to values after `.unwrap()`.

</details>
59 changes: 59 additions & 0 deletions src/control-flow-basics/break-continue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
minutes: 5
---

# `break` and `continue`

If you want to exit any kind of loop early, use
[`break`](https://doc.rust-lang.org/reference/expressions/loop-expr.html#break-expressions).
For `loop`, this can take an optional expression that becomes the value of the `loop` expression.

If you want to immediately start
the next iteration use [`continue`](https://doc.rust-lang.org/reference/expressions/loop-expr.html#continue-expressions).

```rust,editable
fn main() {
let (mut a, mut b) = (100, 52);
let result = loop {
if a == b {
break a;
}
if a < b {
b -= a;
} else {
a -= b;
}
};
println!("{result}");
}
```

Both `continue` and `break` can optionally take a label argument which is used
to break out of nested loops:

```rust,editable
fn main() {
'outer: for x in 1..5 {
println!("x: {x}");
let mut i = 0;
while i < x {
println!("x: {x}, i: {i}");
i += 1;
if i == 3 {
break 'outer;
}
}
}
}
```

In this case we break the outer loop after 3 iterations of the inner loop.

<details>

* Note that `loop` is the only looping construct which returns a non-trivial
value. This is because it's guaranteed to be entered at least once (unlike
`while` and `for` loops).

</details>

58 changes: 58 additions & 0 deletions src/control-flow-basics/conditionals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
minutes: 5
---

# Conditionals

Much of the Rust syntax will be familiar to you from C, C++ or Java:

* Blocks are delimited by curly braces.
* Line comments are started with `//`, block comments are delimited by `/* ...
*/`.
* Keywords like `if` and `while` work the same.
* Variable assignment is done with `=`, comparison is done with `==`.

## `if` expressions

You use [`if`
expressions](https://doc.rust-lang.org/reference/expressions/if-expr.html#if-expressions)
exactly like `if` statements in other languages:

```rust,editable
fn main() {
let x = 10;
if x < 20 {
println!("small");
} else if x < 100 {
println!("biggish");
} else {
println!("huge");
}
}
```

In addition, you can use `if` as an expression. The last expression of each
block becomes the value of the `if` expression:


```rust,editable
fn main() {
let x = 10;
let size = if x < 20 {
"small"
} else {
"large"
};
println!("number size: {}", size);
}
```

<details>

Because `if` is an expression and must have a particular type, both of its branch blocks must have the same type. Show what happens if you add `;` after `"small"` in the second example.

When `if` is used in an expression, the expression must have a `;` to separate
it from the next statement. Remove the `;` before `println!` to see the compiler
error.

</details>
Loading

0 comments on commit 0592424

Please sign in to comment.