# 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"}
    166 views