Skip to content

Commit 4eb85f5

Browse files
committed
add slides about panicking and string formatting
1 parent b0ae4f7 commit 4eb85f5

File tree

6 files changed

+887
-0
lines changed

6 files changed

+887
-0
lines changed

content/lessons/02_ownership/dont_panic/dont_panic.html

Lines changed: 322 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
---
2+
marp: true
3+
author: Wojciech Przytuła
4+
backgroundColor: black
5+
color: grey
6+
transition: fade
7+
theme: gaia
8+
style: |
9+
pre {
10+
background-color: black;
11+
border-style: solid;
12+
border-color: grey;
13+
},
14+
15+
code {
16+
background-color: black;
17+
}
18+
---
19+
20+
# Don't panic
21+
22+
...or how should your Rust program behave when faced a critical error.
23+
24+
---
25+
26+
## A situation which unfortunately happens too often...
27+
28+
```rust
29+
// This function returns Order, so we are forced to return
30+
// an Order even for incorrect inputs.
31+
fn create_order(num_dishes: usize) -> Order {
32+
if num_dishes < Order::MAXIMUM_NUM_DISHES {
33+
Ok(Order::new(num_dishes))
34+
} else {
35+
// ??? Order::INVALID ???
36+
}
37+
}
38+
39+
40+
```
41+
42+
---
43+
44+
## LET'S PANIC!
45+
46+
```rust
47+
// This function returns Order, so we are forced to return
48+
// an Order even for incorrect inputs.
49+
fn create_order(num_dishes: usize) -> Order {
50+
if num_dishes < Order::MAXIMUM_NUM_DISHES {
51+
Ok(Order::new(num_dishes))
52+
} else {
53+
panic!("Too many dishes for a single Order")
54+
// This either unwinds the stack or aborts the program immediately.
55+
// (configurable in Cargo.toml of your project)
56+
}
57+
}
58+
59+
60+
```
61+
62+
---
63+
64+
## Hmm, maybe let's reconsider this...
65+
66+
```rust
67+
/// Error signifying that there are too many dishes to fit in an Order.
68+
struct TooManyDishes;
69+
70+
// This function returns Result, so that we are not forced to return
71+
// an Order for incorrect inputs - we just return Err.
72+
fn create_order(num_dishes: usize) -> Result<Order, TooManyDishes> {
73+
if num_dishes < Order::MAXIMUM_NUM_DISHES {
74+
Ok(Order::new(num_dishes))
75+
} else {
76+
Err(TooManyDishes)
77+
}
78+
}
79+
80+
81+
```
82+
83+
---
84+
85+
## Another common case - `Option`/`Result`
86+
87+
```rust
88+
struct DivisionByZero;
89+
90+
fn div(dividend: i32, divisor: i32) -> Result<i32, DivisionByZero> {
91+
if divisor == 0 {
92+
// It is idiomatic to return errors after failed checks early in an imperative way, using explicit `return`.
93+
return Err(DivisionByZero)
94+
}
95+
96+
// It is idiomatic to have the "happy path" (the no-errors scenario) linear and using functional syntax.
97+
Ok(dividend / divisor)
98+
}
99+
fn main() {
100+
let res: Result<i32, DivisionByZero> = div(2137, 42);
101+
102+
// We are 100% sure division by 42 can't fail, so let's use this shorthand.
103+
let quotient = res.unwrap();
104+
105+
// This is equivalent to:
106+
let quotient = match res {
107+
Ok(x) => x,
108+
Err(err) => panic!("called `Result::unwrap()` on an `Err` value: {:?}", err),
109+
}
110+
}
111+
```
112+
113+
---
114+
115+
### Let's encode more guarantees in the type system!
116+
117+
```rust
118+
use std::num::NonZeroI32;
119+
120+
fn div(dividend: i32, divisor: NonZeroI32) -> i32 {
121+
dividend / divisor // Nothing can get broken here!
122+
}
123+
fn main() {
124+
// let quotient = div(2137, 42); // This would not type check, because 42 is not NonZeroI32.
125+
126+
// We have to create a NonZeroI32 instance:
127+
let non_zero_divisor_opt: Option<NonZeroI32> = NonZeroI32::new(42);
128+
129+
// We are 100% sure 42 is not 0, so let's use this shorthand.
130+
let non_zero_divisor = non_zero_divisor_opt.unwrap();
131+
132+
// This is equivalent to:
133+
let non_zero_divisor = match non_zero_divisor_opt {
134+
Some(x) => x,
135+
None => panic!("called `Option::unwrap()` on a `None` value"),
136+
}
137+
138+
let quotient = div(2137, non_zero_divisor);
139+
}
140+
```
141+
142+
---
143+
144+
### But wait, we ended up with an `unwrap()` anyway. Did we then improve at all?
145+
146+
Actually, yes. Now, the function (here: `div()`) with some (possibly complex) logic **only accepts** (so that the compiler verifies that in compile-time) valid arguments. This way, the function's code can be simpler (no invalid-input-related error handling). Also, it's easier to convince yourself that your number is nonzero that to make sure that it upholds all guarantees required in the docs of the function containing logic.
147+
148+
---
149+
150+
## To `panic` or not to `panic`
151+
152+
Don't panic:
153+
- if the failure is caused by bad user input - you don't want to open up a highway for DoS attackers, do you?
154+
- if the failure only affects some task and not the program in general, e.g. when the server returned bad data for a request of one of the users; others are unaffected;
155+
- if the failure is recoverable (there is a reasonable action to be done in such situation, e.g. give user the default value when the server can't be queried for the actual value);
156+
157+
---
158+
159+
## To `panic` or not to `panic`
160+
161+
Do panic:
162+
- if the failure is for sure caused by a bug in your code. It makes sense to inform the whole world that you wrote deficient software, by yelling at them. More seriously, this shortens the feedback loop and bugs are fixed earlier, instead of silently damaging production;
163+
- if the failure is not recoverable (there is no hope, the program is broken, *R.I.P.*, only the famous *restart* could help here);
164+
165+
---
Binary file not shown.

content/lessons/02_ownership/string_formatting/string_formatting.html

Lines changed: 251 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
---
2+
marp: true
3+
author: Wojciech Przytuła
4+
backgroundColor: black
5+
color: grey
6+
transition: fade
7+
theme: gaia
8+
style: |
9+
pre {
10+
background-color: black;
11+
border-style: solid;
12+
border-color: grey;
13+
},
14+
15+
code {
16+
background-color: black;
17+
}
18+
---
19+
20+
# String formatting
21+
22+
...printf(), sprintf() equivalents & related topics.
23+
24+
---
25+
26+
## Simplest possible output from Rust program
27+
28+
```rust
29+
fn main() {
30+
println!("Hello stdout!");
31+
eprintln!("Hello stderr!");
32+
}
33+
```
34+
35+
---
36+
37+
### Formatted output (using `Display` trait)
38+
39+
```rust
40+
fn agriculture() {
41+
let num_animals = 42_usize;
42+
let animal_name = "ducks";
43+
44+
println!("We have {} {} in our farm.", num_animals, animal_name);
45+
46+
let s: String = format!(
47+
"Nothing is better than {0} {2}, except for {1} {2},",
48+
num_animals, num_animals + 1, animal_name
49+
);
50+
51+
// Minimal assert.
52+
assert!(num_animals >= 42);
53+
// assert with a custom panic message.
54+
assert!(
55+
num_animals >= 42,
56+
"Too few animals in our farm :( - only {} {}", num_animals, animal_name
57+
);
58+
}
59+
```
60+
61+
---
62+
63+
### Formatted output (using `Debug` trait)
64+
65+
```rust
66+
fn agriculture() {
67+
let animals: &[&str] = &["Azor", "Mućka"];
68+
69+
// Does not compile: &[&str] does not implement Display.
70+
// println!("We have the following animals in our farm: {}", animals);
71+
72+
// Concise printing for debug purposes:
73+
println!("We have the following animals in our farm: {:?}", animals);
74+
// Outputs:
75+
// We have the following animals in our farm: ["Azor", "Mućka"]
76+
77+
// Pretty-printing for debug purposes:
78+
println!("We have the following animals in our farm: {:#?}", animals);
79+
// Outputs:
80+
// We have the following animals in our farm: [
81+
// "Azor",
82+
// "Mućka"
83+
// ]
84+
}
85+
```
86+
87+
### Various ways to panic
88+
89+
```rust
90+
fn panicking_is_fun() {
91+
panic!("A general panic.");
92+
assert!(false, "An assertion failed.");
93+
94+
unimplemented!("This code is not implemented.");
95+
todo!("This code is not YET implemented - it's going to change.");
96+
97+
unreachable!("We should never reach this line of code!");
98+
}
99+
```
100+
101+
---
102+
103+
### Memory backing considerations
104+
105+
```rust
106+
fn agriculture() {
107+
let animals: &[&str] = &["Azor", "Mućka"];
108+
109+
let animals: [&str; 2] = ["Azor", "Mućka"];
110+
let animals: &[&str] = &animals;
111+
112+
let animals: Vec<&str> = vec!["Azor", "Mućka"];
113+
114+
static ANIMALS: [&str; 2] = ["Azor", "Mućka"];
115+
static ANIMALS_SLICE: &[&str] = &ANIMALS;
116+
117+
let animals: Vec<&str> = vec!["Azor", "Mućka"];
118+
let animals_slice: &[&str] = &animals;
119+
120+
let animals: Vec<String> = vec!["Azor".into(), "Mućka".into()];
121+
}
122+
```
123+
124+
---
125+
126+
### Memory backing considerations - with hints
127+
128+
```rust
129+
fn agriculture() {
130+
let animals: &[&str] = &["Azor", "Mućka"]; // stack-allocated stack-backed slice.
131+
132+
// stack-allocated array (of statically-allocated strings).
133+
let animals: [&str; 2] = ["Azor", "Mućka"];
134+
let animals: &[&str] = &animals; // stack-allocated stack-backed slice.
135+
136+
let animals: Vec<&str> = vec!["Azor", "Mućka"]; // stack-allocated heap-backed slice.
137+
138+
static ANIMALS: [&str; 2] = ["Azor", "Mućka"]; // statically-allocated array.
139+
static ANIMALS_SLICE: &[&str] = &ANIMALS; // statically-allocated statically-backed slice.
140+
141+
let animals: Vec<&str> = vec!["Azor", "Mućka"]; // stack-allocated heap-backed Vec.
142+
let animals_slice: &[&str] = &animals; // stack-allocated heap-backed slice.
143+
144+
// stack-allocated heap-backed Vec of heap-allocated strings.
145+
let animals: Vec<String> = vec!["Azor".into(), "Mućka".into()];
146+
}
147+
```
148+
149+
---
Binary file not shown.

0 commit comments

Comments
 (0)