You've already forked comprehensive-rust
mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-06-24 17:56:45 +02:00
Comprehensive Rust v2 (#1073)
I've taken some work by @fw-immunant and others on the new organization of the course and condensed it into a form amenable to a text editor and some computational analysis. You can see the inputs in `course.py` but the interesting bits are the output: `outline.md` and `slides.md`. The idea is to break the course into more, smaller segments with exercises at the ends and breaks in between. So `outline.md` lists the segments, their duration, and sums those durations up per-day. It shows we're about an hour too long right now! There are more details of the segments in `slides.md`, or you can see mostly the same stuff in `course.py`. This now contains all of the content from the v1 course, ensuring both that we've covered everything and that we'll have somewhere to redirect every page. Fixes #1082. Fixes #1465. --------- Co-authored-by: Nicole LeGare <dlegare.1001@gmail.com> Co-authored-by: Martin Geisler <mgeisler@google.com>
This commit is contained in:
committed by
GitHub
parent
ea204774b6
commit
6d19292f16
9
src/control-flow-basics/Cargo.toml
Normal file
9
src/control-flow-basics/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "control-flow-basics"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
name = "collatz"
|
||||
path = "exercise.rs"
|
58
src/control-flow-basics/blocks-and-scopes.md
Normal file
58
src/control-flow-basics/blocks-and-scopes.md
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
minutes: 10
|
||||
---
|
||||
|
||||
# Blocks and Scopes
|
||||
|
||||
## 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}");
|
||||
z - y
|
||||
};
|
||||
println!("x: {x}");
|
||||
}
|
||||
```
|
||||
|
||||
If the last expression ends with `;`, then the resulting value and type is `()`.
|
||||
|
||||
## Scopes and Shadowing
|
||||
|
||||
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() {
|
||||
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>
|
||||
|
||||
* 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
src/control-flow-basics/break-continue.md
Normal file
59
src/control-flow-basics/break-continue.md
Normal file
@ -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
src/control-flow-basics/conditionals.md
Normal file
58
src/control-flow-basics/conditionals.md
Normal file
@ -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>
|
35
src/control-flow-basics/exercise.md
Normal file
35
src/control-flow-basics/exercise.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
minutes: 30
|
||||
---
|
||||
|
||||
# Exercise: Collatz Sequence
|
||||
|
||||
The [Collatz Sequence](https://en.wikipedia.org/wiki/Collatz_conjecture) is
|
||||
defined as follows, for an arbitrary n<sub>1</sub> greater than zero:
|
||||
|
||||
- If _n<sub>i</sub>_ is 1, then the sequence terminates at _n<sub>i</sub>_.
|
||||
- If _n<sub>i</sub>_ is even, then _n<sub>i+1</sub> = n<sub>i</sub> / 2_.
|
||||
- If _n<sub>i</sub>_ is odd, then _n<sub>i+1</sub> = 3 * n<sub>i</sub> + 1_.
|
||||
|
||||
For example, beginning with _n<sub>1</sub>_ = 3:
|
||||
* 3 is odd, so _n<sub>2</sub>_ = 3 * 3 + 1 = 10;
|
||||
* 10 is even, so _n<sub>3</sub>_ = 10 / 2 = 5;
|
||||
* 5 is odd, so _n<sub>4</sub>_ = 3 * 15 + 1 = 16;
|
||||
* 16 is even, so _n<sub>5</sub>_ = 16 / 2 = 8;
|
||||
* 8 is even, so _n<sub>6</sub>_ = 8 / 2 = 4;
|
||||
* 4 is even, so _n<sub>7</sub>_ = 4 / 2 = 2;
|
||||
* 2 is even, so _n<sub>8</sub>_ = 1; and
|
||||
* the sequence terminates.
|
||||
|
||||
Write a function to calculate the length of the collatz sequence for a given
|
||||
initial `n`.
|
||||
|
||||
```rust,should_panic
|
||||
{{#include exercise.rs:collatz_length}}
|
||||
todo!("Implement this")
|
||||
}
|
||||
|
||||
{{#include exercise.rs:main}}
|
||||
todo!("Implement this")
|
||||
}
|
||||
```
|
39
src/control-flow-basics/exercise.rs
Normal file
39
src/control-flow-basics/exercise.rs
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// ANCHOR: solution
|
||||
// ANCHOR: collatz_length
|
||||
/// Determine the length of the collatz sequence beginning at `n`.
|
||||
fn collatz_length(mut n: i32) -> u32 {
|
||||
// ANCHOR_END: collatz_length
|
||||
let mut len = 1;
|
||||
while n > 1 {
|
||||
n = if n % 2 == 0 { n / 2 } else { 3 * n + 1 };
|
||||
len += 1;
|
||||
}
|
||||
len
|
||||
}
|
||||
|
||||
// ANCHOR: tests
|
||||
#[test]
|
||||
fn test_collatz_length() {
|
||||
assert_eq!(collatz_length(11), 15);
|
||||
}
|
||||
// ANCHOR_END: tests
|
||||
|
||||
// ANCHOR: main
|
||||
fn main() {
|
||||
// ANCHOR_END: main
|
||||
println!("Length: {}", collatz_length(11));
|
||||
}
|
32
src/control-flow-basics/functions.md
Normal file
32
src/control-flow-basics/functions.md
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
minutes: 3
|
||||
---
|
||||
|
||||
# Functions
|
||||
|
||||
<!-- mdbook-xgettext: skip -->
|
||||
```rust,editable
|
||||
fn gcd(a: u32, b: u32) -> u32 {
|
||||
if b > 0 {
|
||||
gcd(b, a % b)
|
||||
} else {
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("gcd: {}", gcd(143, 52));
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
* Declaration parameters are followed by a type (the reverse of some programming languages), then a return type.
|
||||
* The last expression in a function body (or any block) becomes the return value. Simply omit the `;` at the end of the expression.
|
||||
The `return` keyword can be used for early return, but the "bare value" form is idiomatic at the end of a function (refactor `gcd` to use a `return`).
|
||||
* Some functions have no return value, and return the 'unit type', `()`. The compiler will infer this if the `-> ()` return type is omitted.
|
||||
* Overloading is not supported -- each function has a single implementation.
|
||||
* Always takes a fixed number of parameters. Default arguments are not supported. Macros can be used to support variadic functions.
|
||||
* Always takes a single set of parameter types. These types can be generic, which will be covered later.
|
||||
|
||||
</details>
|
61
src/control-flow-basics/loops.md
Normal file
61
src/control-flow-basics/loops.md
Normal file
@ -0,0 +1,61 @@
|
||||
---
|
||||
minutes: 5
|
||||
---
|
||||
|
||||
# Loops
|
||||
|
||||
There are three looping keywords in Rust: `while`, `loop`, and `for`:
|
||||
|
||||
## `while`
|
||||
|
||||
The [`while` keyword](https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-loops)
|
||||
works much like in other languages, executing the loop body as long as the
|
||||
condition is true.
|
||||
|
||||
```rust,editable
|
||||
fn main() {
|
||||
let mut x = 200;
|
||||
while x >= 10 {
|
||||
x = x / 2;
|
||||
}
|
||||
println!("Final x: {x}");
|
||||
}
|
||||
```
|
||||
|
||||
## `for`
|
||||
|
||||
The [`for` loop](https://doc.rust-lang.org/std/keyword.for.html) iterates over
|
||||
ranges of values:
|
||||
|
||||
```rust,editable
|
||||
fn main() {
|
||||
for x in 1..5 {
|
||||
println!("x: {x}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `loop`
|
||||
|
||||
The [`loop` statement](https://doc.rust-lang.org/std/keyword.loop.html) just
|
||||
loops forever, until a `break`.
|
||||
|
||||
```rust,editable
|
||||
fn main() {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
i += 1;
|
||||
println!("{i}");
|
||||
if i > 100 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
* We will discuss iteration later; for now, just stick to range expressions.
|
||||
* Note that the `for` loop only iterates to `4`. Show the `1..=5` syntax for an inclusive range.
|
||||
|
||||
</details>
|
45
src/control-flow-basics/macros.md
Normal file
45
src/control-flow-basics/macros.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
minutes: 2
|
||||
---
|
||||
|
||||
# Macros
|
||||
|
||||
Macros are expanded into Rust code during compilation, and can take a variable
|
||||
number of arguments. They are distinguished by a `!` at the end. The Rust
|
||||
standard library includes an assortment of useful macros.
|
||||
|
||||
* `println!(format, ..)` prints a line to standard output, applying formatting described in [`std::fmt`](https://doc.rust-lang.org/std/fmt/index.html).
|
||||
* `format!(format, ..)` works just like `println!` but returns the result as a string.
|
||||
* `dbg!(expression)` logs the value of the expression and returns it.
|
||||
* `todo!()` marks a bit of code as not-yet-implemented. If executed, it will panic.
|
||||
* `unreachable!()` marks a bit of code as unreachable. If executed, it will panic.
|
||||
|
||||
```rust,editable
|
||||
fn factorial(n: u32) -> u32 {
|
||||
let mut product = 1;
|
||||
for i in 1..=n {
|
||||
product *= dbg!(i);
|
||||
}
|
||||
product
|
||||
}
|
||||
|
||||
fn fizzbuzz(n: u32) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let n = 13;
|
||||
println!("{n}! = {}", factorial(4));
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
The takeaway from this section is that these common conveniences exist, and how
|
||||
to use them. Why they are defined as macros, and what they expand to, is not
|
||||
especially critical.
|
||||
|
||||
The course does not cover defining macros, but a later section will describe
|
||||
use of derive macros.
|
||||
|
||||
</details>
|
5
src/control-flow-basics/solution.md
Normal file
5
src/control-flow-basics/solution.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Solution
|
||||
|
||||
```rust,editable
|
||||
{{#include exercise.rs:solution}}
|
||||
```
|
Reference in New Issue
Block a user