Skip to content

Commit 47d1995

Browse files
authored
Project feedback (#54)
* Project feedback * Project feedback
1 parent 4241c25 commit 47d1995

File tree

1 file changed

+210
-0
lines changed
  • content/lessons/12_project_feedback

1 file changed

+210
-0
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
+++
2+
title = "Project feedback"
3+
date = 2022-12-29
4+
weight = 1
5+
[extra]
6+
lesson_date = 2022-12-29
7+
+++
8+
9+
# Project feedback
10+
11+
## Unwrapping options/results
12+
13+
Always ask yourself twice if you really need to unwrap. In most cases, you don't have to. Use pattern matching instead,
14+
as it provides a static guarantee that the value is present.
15+
16+
Pattern matching prevents you from writing code like this:
17+
18+
```rust
19+
fn main() {
20+
let x: Option<i32> = some_function();
21+
22+
if x.is_some() {
23+
println!("x is {}", x.unwrap());
24+
}
25+
26+
// Let's say this line was added later and/or you forgot to put it in the if statement.
27+
do_something(x.unwrap()); // this will blow up if x == None!
28+
}
29+
```
30+
31+
Instead, you can write:
32+
33+
```rust
34+
fn main() {
35+
let x: Option<i32> = some_function();
36+
37+
if let Some(x) = x {
38+
println!("x is {}", x);
39+
do_something(x);
40+
}
41+
}
42+
```
43+
44+
## Question mark operator
45+
46+
In methods that return `Result` or `Option`, you can use the question mark operator to return early if the value is `None` or `Err`.
47+
See: https://doc.rust-lang.org/rust-by-example/std/result/question_mark.html
48+
49+
## Logging
50+
51+
You can use the [log](https://crates.io/crates/log) crate to log messages. It's better than `println!` because it
52+
can be easily turned off. It also allows you to use different severity levels (e.g. `info`, `warn`, `error`) and only
53+
log messages above a certain level.
54+
55+
## &String vs &str
56+
57+
See https://doc.rust-lang.org/book/ch04-03-slices.html#string-slices-as-parameters
58+
In general, if you want to pass a reference to a string, use `&str` instead of `&String`.
59+
60+
## Use current versions of dependencies
61+
62+
You can use [cargo upgrades](https://crates.io/crates/cargo-upgrades) to check for outdated dependencies.
63+
64+
## If your project has separate binaries, use multiple binaries or a workspace
65+
66+
You can have multiple binaries in a single cargo project. Simply place them in the `src/bin` directory.
67+
You can run them with `cargo run --bin <name>`. Alternatively, you can setup a
68+
[workspace](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html).
69+
70+
## Run clippy & cargo fmt
71+
72+
This should have become a habit by now. You can disable clippy warnings for a single item with `#[allow(clippy::...)]`,
73+
but in most cases you shouldn't do that.
74+
75+
## If you need to escape characters in a string, use raw strings
76+
77+
See https://doc.rust-lang.org/reference/tokens.html#raw-string-literals
78+
79+
## How to handle errors?
80+
81+
Short: https://kerkour.com/rust-error-handling
82+
83+
Long: https://www.lpalmieri.com/posts/error-handling-rust/
84+
85+
## Don't pass around locked mutex's contents
86+
87+
If you have a mutex, you can use `lock()` to get a guard that will unlock the mutex when it goes out of scope.
88+
But don't pass the contents of the guard to functions that can block (unless the mutex _must_ be locked for
89+
the entire duration of the function).
90+
Instead of:
91+
92+
```rust
93+
use std::sync::Mutex;
94+
use std::thread;
95+
use std::time::Duration;
96+
use std::time::Instant;
97+
98+
fn handle_function(counter: &mut i32) {
99+
thread::sleep(Duration::from_secs(1));
100+
*counter += 1;
101+
thread::sleep(Duration::from_secs(1));
102+
}
103+
104+
fn main() {
105+
let counter = Mutex::new(1);
106+
107+
thread::scope(|s| {
108+
for i in 0..10 {
109+
let counter = &counter;
110+
s.spawn(move || {
111+
println!("Thread {i} started");
112+
let now = Instant::now();
113+
let mut counter = counter.lock().unwrap();
114+
handle_function(&mut counter); // lock is held for 2 seconds
115+
println!("Thread {i} finished after {}s", now.elapsed().as_secs());
116+
});
117+
}
118+
})
119+
}
120+
```
121+
122+
You should do this:
123+
124+
```rust
125+
use std::sync::Mutex;
126+
use std::thread;
127+
use std::time::Duration;
128+
use std::time::Instant;
129+
130+
fn handle_function(counter: &Mutex<i32>) { // <-- changed
131+
thread::sleep(Duration::from_secs(1));
132+
{
133+
let mut counter = counter.lock().unwrap(); // <-- changed
134+
*counter += 1;
135+
// lock is held only for the duration of the block
136+
// it is important to create a new scope here, otherwise the lock would be held for another second
137+
}
138+
thread::sleep(Duration::from_secs(1));
139+
}
140+
141+
fn main() {
142+
let counter = Mutex::new(1);
143+
144+
thread::scope(|s| {
145+
for i in 0..10 {
146+
let counter = &counter;
147+
s.spawn(move || {
148+
println!("Thread {i} started");
149+
let now = Instant::now();
150+
handle_function(counter); // <-- changed! we don't lock here
151+
println!("Thread {i} finished after {}s", now.elapsed().as_secs());
152+
});
153+
}
154+
})
155+
}
156+
157+
```
158+
159+
Compare the output of the two programs. The first one will take 20 seconds to finish, while the second one will take 2 seconds.
160+
161+
First one:
162+
163+
```
164+
Thread 1 started
165+
Thread 0 started
166+
Thread 2 started
167+
Thread 3 started
168+
Thread 4 started
169+
Thread 5 started
170+
Thread 6 started
171+
Thread 7 started
172+
Thread 8 started
173+
Thread 9 started
174+
Thread 1 finished after 2s
175+
Thread 0 finished after 4s
176+
Thread 2 finished after 6s
177+
Thread 3 finished after 8s
178+
Thread 4 finished after 10s
179+
Thread 5 finished after 12s
180+
Thread 6 finished after 14s
181+
Thread 7 finished after 16s
182+
Thread 8 finished after 18s
183+
Thread 9 finished after 20s
184+
185+
```
186+
187+
Second one:
188+
189+
```
190+
Thread 0 started
191+
Thread 2 started
192+
Thread 1 started
193+
Thread 3 started
194+
Thread 4 started
195+
Thread 5 started
196+
Thread 6 started
197+
Thread 7 started
198+
Thread 8 started
199+
Thread 9 started
200+
Thread 1 finished after 2s
201+
Thread 2 finished after 2s
202+
Thread 0 finished after 2s
203+
Thread 3 finished after 2s
204+
Thread 4 finished after 2s
205+
Thread 5 finished after 2s
206+
Thread 6 finished after 2s
207+
Thread 7 finished after 2s
208+
Thread 8 finished after 2s
209+
Thread 9 finished after 2s
210+
```

0 commit comments

Comments
 (0)