mod side_table {
use std::sync::atomic::{AtomicPtr, Ordering};
static STORAGE: AtomicPtr<String> = AtomicPtr::new(std::ptr::null_mut());
pub fn set<'a: 'a>(x: &'a String) {
// TODO: assert that this is the first time we've called `set` to avoid
// overwriting the value.
STORAGE.store((&raw const *x).cast_mut(), Ordering::Release)
}
/// # Safety
///
/// This must only be called after `set` has been called for the lifetime `'a`.
pub unsafe fn get<'a>() -> &'a String {
unsafe { &*STORAGE.load(Ordering::Acquire) }
}
}
fn yikes<'a>(x: &'a String) -> impl FnOnce() + use<> {
side_table::set::<'a>(x);
|| {
// SAFETY: We've put a reference in the storage which is
// valid for the whole lifetime `'a`
let print_me = unsafe { side_table::get::<'a>() };
println!("{print_me}");
}
}
fn main() {
let closure = {
let temp = String::from("temporary");
yikes(&temp)
};
closure();
}
I believe the safety comment in the closure is correct and this should be sound. It explicitly checks that get is called with the lifetime 'a.
This is the even more general version of #84366 #112905
I discussed closure outives requirements with @BoxyUwU recently and there we considered that longterm, we would like the following rules:
- closures have to outlive all generic parameters mentioned in their signature
- for any lifetime/type parameter referenced in the body of the closure, we check that this generic parameter is outlived by at least one element of the signature
- which implies that the closure can only ever be used while these parameters are still valid
I believe the safety comment in the closure is correct and this should be sound. It explicitly checks that
getis called with the lifetime'a.This is the even more general version of #84366 #112905
I discussed closure outives requirements with @BoxyUwU recently and there we considered that longterm, we would like the following rules: