1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-11-30 09:08: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:
Dustin J. Mitchell
2023-11-29 10:39:24 -05:00
committed by GitHub
parent ea204774b6
commit 6d19292f16
309 changed files with 6807 additions and 4281 deletions

9
src/iterators/Cargo.toml Normal file
View File

@@ -0,0 +1,9 @@
[package]
name = "iterators"
version = "0.1.0"
edition = "2021"
publish = false
[[bin]]
name = "offset-differences"
path = "exercise.rs"

22
src/iterators/exercise.md Normal file
View File

@@ -0,0 +1,22 @@
---
minutes: 30
---
# Exercise: Iterator Method Chaining
In this exercise, you will need to find and use some of the provided methods in
the [`Iterator`][1] trait to implement a complex calculation.
Copy the following code to <https://play.rust-lang.org/> and make the tests
pass. Use an iterator expression and `collect` the result to construct the
return value.
```rust
{{#include exercise.rs:offset_differences}}
unimplemented!()
}
{{#include exercise.rs:unit-tests}}
```
[1]: https://doc.rust-lang.org/std/iter/trait.Iterator.html

66
src/iterators/exercise.rs Normal file
View File

@@ -0,0 +1,66 @@
// 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.
#![allow(dead_code)]
// ANCHOR: solution
// ANCHOR: offset_differences
/// Calculate the differences between elements of `values` offset by `offset`, wrapping
/// around from the end of `values` to the beginning.
///
/// Element `n` of the result is `values[(n+offset)%len] - values[n]`.
fn offset_differences<N>(offset: usize, values: Vec<N>) -> Vec<N>
where
N: Copy + std::ops::Sub<Output = N>,
{
// ANCHOR_END: offset_differences
let a = (&values).into_iter();
let b = (&values).into_iter().cycle().skip(offset);
a.zip(b).map(|(a, b)| *b - *a).take(values.len()).collect()
}
// ANCHOR: unit-tests
#[test]
fn test_offset_one() {
assert_eq!(offset_differences(1, vec![1, 3, 5, 7]), vec![2, 2, 2, -6]);
assert_eq!(offset_differences(1, vec![1, 3, 5]), vec![2, 2, -4]);
assert_eq!(offset_differences(1, vec![1, 3]), vec![2, -2]);
}
#[test]
fn test_larger_offsets() {
assert_eq!(offset_differences(2, vec![1, 3, 5, 7]), vec![4, 4, -4, -4]);
assert_eq!(offset_differences(3, vec![1, 3, 5, 7]), vec![6, -2, -2, -2]);
assert_eq!(offset_differences(4, vec![1, 3, 5, 7]), vec![0, 0, 0, 0]);
assert_eq!(offset_differences(5, vec![1, 3, 5, 7]), vec![2, 2, 2, -6]);
}
#[test]
fn test_custom_type() {
assert_eq!(
offset_differences(1, vec![1.0, 11.0, 5.0, 0.0]),
vec![10.0, -6.0, -5.0, 1.0]
);
}
#[test]
fn test_degenerate_cases() {
assert_eq!(offset_differences(1, vec![0]), vec![0]);
assert_eq!(offset_differences(1, vec![1]), vec![0]);
let empty: Vec<i32> = vec![];
assert_eq!(offset_differences(1, empty), vec![]);
}
// ANCHOR_END: unit-tests
fn main() {}

View File

@@ -0,0 +1,46 @@
---
minutes: 5
---
# FromIterator
[`FromIterator`][1] lets you build a collection from an [`Iterator`][2].
```rust,editable
fn main() {
let primes = vec![2, 3, 5, 7];
let prime_squares = primes
.into_iter()
.map(|prime| prime * prime)
.collect::<Vec<_>>();
println!("prime_squares: {prime_squares:?}");
}
```
<details>
`Iterator` implements
```rust,ignore
fn collect<B>(self) -> B
where
B: FromIterator<Self::Item>,
Self: Sized
```
There are two ways to specify `B` for this method:
* With the "turbofish": `some_iterator.collect::<COLLECTION_TYPE>()`, as
shown. The `_` shorthand used here lets Rust infer the type of the `Vec`
elements.
* With type inference: `let prime_squares: Vec<_> =
some_iterator.collect()`. Rewrite the example to use this form.
There are basic implementations of `FromIterator` for `Vec`, `HashMap`, etc.
There are also more specialized implementations which let you do cool things
like convert an `Iterator<Item = Result<V, E>>` into a `Result<Vec<V>, E>`.
</details>
[1]: https://doc.rust-lang.org/std/iter/trait.FromIterator.html
[2]: https://doc.rust-lang.org/std/iter/trait.Iterator.html

View File

@@ -0,0 +1,84 @@
---
minutes: 5
---
# `IntoIterator`
The `Iterator` trait tells you how to _iterate_ once you have created an
iterator. The related trait
[`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html)
defines how to create an iterator for a type. It is used automatically by the
`for` loop.
```rust,editable
struct Grid {
x_coords: Vec<u32>,
y_coords: Vec<u32>,
}
impl IntoIterator for Grid {
type Item = (u32, u32);
type IntoIter = GridIter;
fn into_iter(self) -> GridIter {
GridIter { grid: self, i: 0, j: 0 }
}
}
struct GridIter {
grid: Grid,
i: usize,
j: usize,
}
impl Iterator for GridIter {
type Item = (u32, u32);
fn next(&mut self) -> Option<(u32, u32)> {
self.i += 1;
if self.i >= self.grid.x_coords.len() {
self.i = 0;
self.j += 1;
if self.j >= self.grid.y_coords.len() {
return None;
}
}
Some((self.grid.x_coords[self.i], self.grid.y_coords[self.j]))
}
}
fn main() {
let grid = Grid {
x_coords: vec![3, 5, 7, 9],
y_coords: vec![10, 20, 30, 40],
};
for (x, y) in grid {
println!("point = {x}, {y}");
}
}
```
<details>
Click through to the docs for `IntoIterator`. Every implementation of
`IntoIterator` must declare two types:
* `Item`: the type to iterate over, such as `i8`,
* `IntoIter`: the `Iterator` type returned by the `into_iter` method.
Note that `IntoIter` and `Item` are linked: the iterator must have the same
`Item` type, which means that it returns `Option<Item>`
The example iterates over all combinations of x and y coordinates.
Try iterating over the grid twice in `main`. Why does this fail? Note that
`IntoIterator::into_iter` takes ownership of `self`.
Fix this issue by implementing `IntoIterator` for `&Grid` and storing a
reference to the `Grid` in `GridIter`.
The same problem can occur for standard library types: `for e in some_vector`
will take ownership of `some_vector` and iterate over owned elements from that
vector. Use `for e in &some_vector` instead, to iterate over references to
elements of `some_vector`.
</details>

View File

@@ -0,0 +1,53 @@
---
minutes: 5
---
<!-- NOTES:
The Iterator trait and basic usage
-->
# Iterators
The [`Iterator`][1] trait supports iterating over values in a collection. It
requires a `next` method and provides lots of methods. Many standard library types
implement `Iterator`, and you can implement it yourself, too:
```rust,editable
struct Fibonacci {
curr: u32,
next: u32,
}
impl Iterator for Fibonacci {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
let new_next = self.curr + self.next;
self.curr = self.next;
self.next = new_next;
Some(self.curr)
}
}
fn main() {
let fib = Fibonacci { curr: 0, next: 1 };
for (i, n) in fib.enumerate().take(5) {
println!("fib({i}): {n}");
}
}
```
<details>
* The `Iterator` trait implements many common functional programming operations over collections
(e.g. `map`, `filter`, `reduce`, etc). This is the trait where you can find all the documentation
about them. In Rust these functions should produce the code as efficient as equivalent imperative
implementations.
* `IntoIterator` is the trait that makes for loops work. It is implemented by collection types such as
`Vec<T>` and references to them such as `&Vec<T>` and `&[T]`. Ranges also implement it. This is why
you can iterate over a vector with `for i in some_vec { .. }` but
`some_vec.next()` doesn't exist.
</details>
[1]: https://doc.rust-lang.org/std/iter/trait.Iterator.html

View File

@@ -0,0 +1,5 @@
# Solution
```rust,editable
{{#include exercise.rs:solution}}
```