You've already forked comprehensive-rust
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:
committed by
GitHub
parent
ea204774b6
commit
6d19292f16
9
src/iterators/Cargo.toml
Normal file
9
src/iterators/Cargo.toml
Normal 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
22
src/iterators/exercise.md
Normal 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
66
src/iterators/exercise.rs
Normal 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() {}
|
||||
46
src/iterators/fromiterator.md
Normal file
46
src/iterators/fromiterator.md
Normal 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
|
||||
84
src/iterators/intoiterator.md
Normal file
84
src/iterators/intoiterator.md
Normal 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>
|
||||
53
src/iterators/iterators.md
Normal file
53
src/iterators/iterators.md
Normal 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
|
||||
5
src/iterators/solution.md
Normal file
5
src/iterators/solution.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Solution
|
||||
|
||||
```rust,editable
|
||||
{{#include exercise.rs:solution}}
|
||||
```
|
||||
Reference in New Issue
Block a user