# Locks and Temporary Lifetime Footguns
Xiangfei Ding
2024
---
## Locks? What are those?
```rust
// sync version
use std::sync::{
Mutex,
RwLock,
MutexGuard, // *
RwLockReadGuard, RwLockWriteGuard, // **
};
// async version
use tokio::sync::{Mutex, RwLock};
use async_std::sync::{Mutex, RwLock};
```
---
##### Locks, continued
```rust
use std::sync::{Mutex, MutexGuard};
let guard: MutexGuard<'_, Vec<Object>> =
my_mutex.lock().unwrap();
guard.push(object);
drop(guard);
```
---
## Temporary lifetime? What's that?
```rust
{
// in order to call compute() ...
a.b().0.compute();
// ~~~~~~~ you need this!
}
```
---
##### Temporary lifetime continued
It also makes your life easier!
```rust
// other.get_objects(): Vec<Object>
match other.get_objects().last() {
Some(last_object) => {
// the `Vec<Object>` from `other.get_objects()`
// is alive here
}
None => {
// the `Vec<Object>` from `other.get_objects()`
// is alive here,
// but we won't bother, right?
}
}
```
---
#### Temporary lifetime: it's getting tricky
```rust=
if let Some(last_object) = other.get_objects().last() {
// the `Vec<Object>` from `other.get_objects()`
// is alive here
} else {
// the `Vec<Object>` from `other.get_objects()`
// is alive here,
// but we won't bother, right?
// ... right?
}
```
---
```rust
let object = &compute();
// ~~~~~~~~~
let object = Object {
reference: &make_another_object(),
// ~~~~~~~~~~~~~~~~~~~~~
..
};
```
---
```rust=
if let Some(value) = &*my_mutex.lock().unwrap() {
// ^~~~~~~~~~~~~~~~~~~~~~~~
// we have a `value` to process
// so it is nice to keep `my_mutex.lock().unwrap()` around
} else {
sleep(60); // ???
}
```
---
Footguns
---
```rust=
let guard = my_mutex.lock().unwrap();
if let Some(value) = &*guard {
// ^~~~~
// we have a `value` to process
// so it is nice to keep `my_mutex.lock().unwrap()` around
} else {
drop(guard); // this is the fix
sleep(60);
}
```
---
```rust=
{
let data = &mut my_mutex.lock().unwrap().data;
// wait! the lock is held until the end of block
// and we have no way to release it
}
```
---
### wait, `let .. = .. else { .. };` works differently today!
```rust
let Some(value) = my_mutex.lock().unwrap().value else {
// apparently lock guard is already dropped here
return;
};
```
---
digression: the RAII-resource point of view
---
Idea: make the lifetime shorter
---
### Community proposal
We can fix `if let`
```rust
if let Some(value) = &*mutex.lock().unwrap() {
// sure, we keep the guard around
} else {
// but we should drop here if the pattern matching fails
}
```
---
Fix `match`?
```rust
match mutex.lock().unwrap().state {
// ^~~~~~~~~~~~~~~~~~~~~ --^ drop here?
State::Idle => {
// lock guard is dropped already
}
}
```
### Maybe no
---
The `match` nichee, aka the single-arm `match`.
```rust
macro_rules! assert_eq {
($left:expr, $right:expr, ..) => {
match ($left, $right) => {
(left, right) => {
...
}
}
}
}
assert_eq!(vec![1].last(), Some(1));
```
---
### ideas?
Early drop or flexible drop: lint when the target type or value can be dropped earlier than necessary
---
### ideas?
Maybe introduce `let .. = .. in ..` to replace the *single-arm `match` case*?
---
### ideas?
???
---
### Thank you for your attention!
Q&A
---
Note: example with if (....is_some())
A table to show applicablility among `if let`, `if`, `let else`, `match`
Add arrows to point at program where values are dropped
Add QR code to playground links
Add titles to slides
{"title":"Locks and Temporary Lifetime Footguns","contributors":"[{\"id\":\"09ec042c-67c9-4202-a7df-0f0c8410e4fa\",\"add\":3946,\"del\":136}]","description":"Xiangfei Ding2024"}