Skip to content

Commit 86431c2

Browse files
tonowakwprzytula
andauthored
lesson 05: change text (#99)
Co-authored-by: Wojciech Przytuła <[email protected]>
1 parent 2f58438 commit 86431c2

File tree

4 files changed

+45
-30
lines changed

4 files changed

+45
-30
lines changed

content/lessons/05_types_reasoning/generics.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22

33
use std::fmt::Debug;
44

5-
// generic enums
5+
// Generic enums.
66
enum OurOption<T> {
77
Some(T),
88
None,
99
}
1010

11-
// generic structs
11+
// Generic structs.
1212
struct Tuple2<T, U> {
1313
x: T,
1414
y: U,
1515
}
1616

17-
// generic implementation
17+
// Generic implementation.
1818
impl<T, U> Tuple2<T, U> {
1919
fn new(x: T, y: U) -> Self {
2020
Self { x, y }
@@ -26,7 +26,7 @@ struct Pair<T> {
2626
y: T,
2727
}
2828

29-
// conditional implementation
29+
// Conditional implementation.
3030
impl<T: PartialOrd + Copy> Pair<T> {
3131
fn largest(&self) -> T {
3232
if self.x > self.y {
@@ -37,7 +37,7 @@ impl<T: PartialOrd + Copy> Pair<T> {
3737
}
3838
}
3939

40-
// alternative syntax
40+
// Alternative syntax.
4141
impl<T> Pair<T>
4242
where
4343
T: PartialOrd + Copy,
@@ -51,19 +51,24 @@ where
5151
}
5252
}
5353

54-
// Here information about the concrete underlying type is preserved.
54+
// The information about the concrete underlying type is preserved.
55+
// If I call it with a `String`, then I get back a `String`.
5556
fn cloning_machine<T: Clone + Debug>(item: &T) -> T {
5657
item.clone()
5758
}
5859

59-
// Here information about the concrete underlying type is erased.
60+
// The information about the concrete underlying type is erased.
6061
// We can only either format or clone the result.
61-
fn erasing_cloning_machine1(item: &(impl Clone + Debug)) -> impl Clone + Debug {
62+
// If I call it with a `String`, then I'll only know that the return type
63+
// implements `Clone + Debug`.
64+
fn erasing_cloning_machine2<T: Clone + Debug>(item: &T) -> impl Clone + Debug {
6265
item.clone()
6366
}
6467

65-
// Ditto.
66-
fn erasing_cloning_machine2<T: Clone + Debug>(item: &T) -> impl Clone + Debug {
68+
// The returned type behaves exactly the same as above (it's the same type, after all)
69+
// and the function has the same requirements for the `item` argument.
70+
// But inside the implementation of the function, we can't use `T` (it's not defined anywhere).
71+
fn erasing_cloning_machine1(item: &(impl Clone + Debug)) -> impl Clone + Debug {
6772
item.clone()
6873
}
6974

content/lessons/05_types_reasoning/generics_fun.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ impl Display for Bar {
2121

2222
impl Default for Bar {
2323
fn default() -> Self {
24-
Bar // well, we have no other choice
24+
Bar // Well, we have no other choice.
2525
}
2626
}
2727

@@ -30,6 +30,9 @@ impl DefaultishablyPrintable<i32> for Foo {}
3030
impl DefaultishablyPrintable<Bar> for Foo {}
3131

3232
fn main() {
33+
// By typing `Foo as DefaultishablyPrintable<i32>`,
34+
// we tell the compiler to treat this `Foo` struct as
35+
// only a `DefaultishablyPrintable<i32>` trait.
3336
<Foo as DefaultishablyPrintable<i32>>::defaultish_print();
3437
<Foo as DefaultishablyPrintable<Bar>>::defaultish_print();
3538
}

content/lessons/05_types_reasoning/index.md

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
+++
22
title = "Reasoning About Types"
3-
date = 2029-01-01
3+
date = 2025-10-20
44
weight = 1
5-
[extra]
6-
lesson_date = 2029-01-01
5+
[extra]
6+
lesson_date = 2025-10-23
77
+++
88

99
# Type traits
@@ -22,9 +22,11 @@ Trait definitions can also be provided with default implementations of behaviors
2222

2323
## What about _derive_?
2424

25-
There is a trait-related thing we have used quite extensively and not explained yet, namely the `#[derive]` attribute. What it does is generate items (in our case a trait implementation) based on the given data definition (here a struct). Below you can find a list of derivable traits from the standard library. Writing derivation rules for user defined traits is also possible, but goes out of the scope of this lesson.
25+
There is a trait-related feature we have used quite extensively but not explained yet, namely the `#[derive]` attribute. When placed above a struct or enum, it tells the compiler to generate an implementation of certain traits automatically. For example, `#[derive(Debug)]` will cause the compiler to create the necessary `impl Debug for YourType { ... }` code behind the scenes, so that your type can be printed with `{:?}` in `println!`.
2626

27-
Derivable traits:
27+
We'll learn about how to make it work with our own traits in the next lessons. For now, you can think of `derive` as a kind of code generator built into the compiler, which is especially useful when the implementation of a trait can be generalized for any type.
28+
29+
Below you can find a list of derivable traits from the standard library.
2830

2931
- Equality traits: `Eq`, `PartialEq` and comparison traits: `Ord` and `PartialOrd`. The `Partial-` versions exist because there are types which don't fulfill the reflexivity requirement of equality (`NaN != NaN`) or do not form a total order (` NaN < 0.0 == false` and `NaN >= 0.0 == false`).
3032

@@ -34,15 +36,15 @@ Derivable traits:
3436

3537
- `Default` - provides a zero-arg constructor function
3638

37-
- `Debug` - provides a formatting of the value which can be used in debugging context. It should _NOT_ be implemented manually. In general, if it's possible to derive the `Debug`, there are no reasons against doing it.
39+
- `Debug` - provides a formatting of the value which can be used in debugging context. Because the `derive` attribute automatically implements a pretty way of formatting, it is discouraged to implement this trait manually. In general, if it's possible to derive the `Debug`, there are no reasons against doing it.
3840

3941
### When is it possible to derive a trait?
4042

4143
When all fields of a struct/variants of an enum implement that trait.
4244

4345
### Should all traits always be derived if it is possible?
4446

45-
No. Although it may be tempting to just slap `#[derive(Clone, Copy)]` everywhere, it would be counter-effective. For example, at some later point you might add a non-Copy field to the struct and your (or, what's worse, someone else's!) code would break. Another example: it makes little sense to use containers as keys in hashmaps or to compare tweets.
47+
No. Although it may be tempting to just slap `#[derive(Clone, Copy)]` everywhere, it would be counter-effective. For example, at some later point you might add a non-`Copy` field to the struct and your (or, what's worse, someone else's!) code would break. Another example: it makes little sense to use containers as keys in hashmaps or to compare tweets.
4648

4749
# Generics
4850

@@ -52,7 +54,7 @@ Suppose we want to find the largest element in a sequence and return it. Very mu
5254

5355
Perfect, it works! Now only twenty more types to go...
5456

55-
Fortunately, Rust gives us a way to avoid all this code duplication and generalize the types we're working on.
57+
Of course, Rust gives us a way to avoid all this code duplication and generalize the types we're working on.
5658

5759
```rust
5860
fn largest<T>(list: &[T]) -> T {
@@ -85,7 +87,7 @@ help: consider restricting type parameter `T`
8587
| ++++++++++++++++++++++
8688
```
8789

88-
Since `T` can be of absolutely any type now, the compiler cannot be sure that operator `>` is defined. This aligns with what we wanted, as without comparing elements we don't have a notion of the largest one either. As always, the compiler comes to our aid:
90+
Since `T` can be of absolutely any type now, the compiler cannot be sure that operator `>` is defined. This aligns with what we wanted, as without comparing elements we don't have a notion of the largest one either. As always, the compiler messages come to our aid:
8991

9092
```rust
9193
fn largest<T: PartialOrd>(list: &[T]) -> T {
@@ -135,7 +137,7 @@ There's a lot more that we can do with generics:
135137

136138
{{ include_code_sample(path="lessons/05_types_reasoning/generics.rs", language="rust") }}
137139

138-
A bit more involved example:
140+
An example where we can specify which generic trait implementation we want to call:
139141

140142
{{ include_code_sample(path="lessons/05_types_reasoning/generics_fun.rs", language="rust") }}
141143

@@ -145,6 +147,7 @@ A bit more involved example:
145147

146148
# Lifetimes
147149

150+
Let's go into a completely different topic now.
148151
Going back to the lesson about ownership, if we try to compile the following code:
149152

150153
```rust
@@ -281,7 +284,7 @@ error[E0597]: `string2` does not live long enough
281284

282285
## Lifetime elision
283286

284-
We now know how to explicitly write lifetime parameters, but you might recall that we don't always have to that. Indeed, Rust will first try to figure out the lifetimes itself, applying a set of predefined rules. We call this _lifetime elision_.
287+
We now know how to explicitly write lifetime parameters, but you might recall that we don't always have to do that. Indeed, Rust will first try to figure out the lifetimes itself, applying a set of predefined rules. We call this _lifetime elision_.
285288

286289
{{ include_code_sample(path="lessons/05_types_reasoning/lifetimes_elision.rs", language="rust") }}
287290

@@ -360,8 +363,12 @@ Example:
360363

361364
- [Polymorphism in Rust](https://oswalt.dev/2021/06/polymorphism-in-rust/)
362365

366+
# Optional reading
367+
368+
- [Rust Blog - Precise capturing `use<..>` syntax](https://blog.rust-lang.org/2024/10/17/Rust-1.82.0/#precise-capturing-use-syntax)
369+
363370
## Assignment 3 (graded)
364371

365-
[Passage Pathing](https://classroom.github.com/a/VTyPdlC2)
372+
[Passage Pathing](https://classroom.github.com/a/XVoSKs94)
366373

367-
Deadline: 30.10.2024 23:59
374+
Deadline: per-group.

content/lessons/05_types_reasoning/static_dynamic_dispatch.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ struct Dog;
66

77
impl Speak for Dog {
88
fn speak(&self) -> &'static str {
9-
"Hau hau" // it's a Polish dog!
9+
"Hau hau" // It's a Polish dog!
1010
}
1111
}
1212

@@ -18,14 +18,14 @@ impl Speak for Human {
1818
}
1919
}
2020

21-
// It works like templates in C++
22-
// A different function will be generated for each T during compilation
23-
// This process is called "monomorphization"
21+
// It works like templates in C++.
22+
// A different function will be generated for each T during compilation.
23+
// This process is called "monomorphization".
2424
fn static_dispatch<T: Speak>(speaking: &T) {
2525
println!("{}!", speaking.speak());
2626
}
2727

28-
// Only one copy of that function will exist in the compiled binary
28+
// Only one copy of that function will exist in the compiled binary.
2929
fn dynamic_dispatch(speaking: &dyn Speak) {
3030
println!("{}!", speaking.speak());
3131
}
@@ -40,7 +40,7 @@ fn main() {
4040
dynamic_dispatch(&dog);
4141
dynamic_dispatch(&human);
4242

43-
// The observable behavior is identical
43+
// The observable behavior is identical.
4444
// Static dispatch in general is a bit faster,
4545
// because there is no need to perform a "vtable lookup".
4646
// But it can also result in bigger binary sizes.

0 commit comments

Comments
 (0)