I am trying to create a library that safely allows having a reference to non-'static data in a 'static context by dynamically holding the reference active until it is no longer accessed. Feel free to just tell me about an existing crate that does this. My crate is called static-newtype
.
With simple references, it works:
use static_newtype::ParentNewtype;
fn main() {
// not available in 'static lifetime
let mut data = 0;
let parent = ParentNewtype::new(&mut data);
let child = parent.child();
// move access to non-'static data into place where static data is required
moo(move || {
println!("{}", {
let child = child.get().unwrap();
*child
});
});
//destructor on ParentNewType blocks until all children are dropped, so the reference is kept active
}
fn moo(test: impl FnOnce() + 'static) {
test();
}
But, when I try to use a non-static struct with non-static references (which I need in my use case), I get errors:
use static_newtype::ParentNewtype;
struct RefContainer<'i> {
u: &'i mut u32,
}
fn main() {
// not available in 'static lifetime
let mut data = 0;
let mut ref_container = RefContainer { u: &mut data };
let parent = ParentNewtype::new(&mut ref_container);
let child = parent.child();
// move access to non-'static data into place where static data is required
moo(move || {
println!("{}", {
let child = child.get().unwrap();
*child.u
});
});
//destructor on ParentNewType blocks until all children are dropped, so the reference is kept active
}
fn moo(test: impl FnOnce() + 'static) {
test();
}
gives
error[E0597]: `data` does not live long enough
--> examples/struct.rs:10:47
|
9 | let mut data = 0;
| -------- binding `data` declared here
10 | let mut ref_container = RefContainer { u: &mut data };
| ^^^^^^^^^ borrowed value does not live long enough
11 |
12 | let parent = ParentNewtype::new(&mut ref_container);
| -------------------------------------- argument requires that `data` is borrowed for `'static`
...
23 | }
| - `data` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
I know that this is because (see my library's code below) I am using the same T
to refer to RefContainer<'a>
, RefContainer<'static>
, and then again when returning from my Deref implementations, despite the fact that these are all different types. But, I do not now how to fix this issue since I do not know how to go between these types when all that is available is a generic parameter T
.
This is related to GATs, but GATs only work with traits and I do not see a way to transform this into something that's sufficiently trait-based for this to work.
My library:
use parking_lot::{ArcRwLockReadGuard, ArcRwLockWriteGuard, RawRwLock, RwLock};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Weak};
pub struct ParentNewtype<'a, T: 'a> {
data: Arc<RwLock<Option<*mut T>>>,
_phantom: PhantomData<&'a ()>,
}
pub struct ChildNewtype<T> {
data: Weak<RwLock<Option<*mut T>>>,
}
pub struct ChildNewtypeReadGuard<T> {
data: ArcRwLockReadGuard<RawRwLock, Option<*mut T>>,
}
pub struct ChildNewtypeWriteGuard<T> {
data: ArcRwLockWriteGuard<RawRwLock, Option<*mut T>>,
}
impl<'a, T: 'a + Sized> ParentNewtype<'a, T> {
pub fn new(data: &'a mut T) -> Self {
Self {
data: Arc::new(RwLock::new(Some(unsafe { std::mem::transmute(data) }))),
_phantom: PhantomData::default(),
}
}
#[inline]
pub fn child(&self) -> ChildNewtype<T> {
ChildNewtype {
data: Arc::downgrade(&self.data),
}
}
}
impl<T> ChildNewtype<T> {
pub fn get(&self) -> Option<ChildNewtypeReadGuard<T>> {
let arc = self.data.upgrade()?;
let data = arc.read_arc();
if data.is_none() {
return None;
}
Some(ChildNewtypeReadGuard { data })
}
pub fn get_mut(&self) -> Option<ChildNewtypeWriteGuard<T>> {
let arc = self.data.upgrade()?;
let data = arc.write_arc();
if data.is_none() {
return None;
}
Some(ChildNewtypeWriteGuard { data })
}
}
impl<T> Deref for ChildNewtypeReadGuard<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
let ptr = self.data.clone().unwrap(); //unwrap will not panic because this is created only via ChildNewtype::get which checks for None, and we hold a & reference to it continuously preventing it from being changed out from under us
let ptr_const = ptr as *const T;
unsafe { &*ptr_const } //safety, we create a reference with the same lifetime as Self, but keeping Self keeps the original data alive via the Drop implementation
}
}
impl<T> Deref for ChildNewtypeWriteGuard<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
let ptr = self.data.clone().unwrap(); //unwrap will not panic because this is created only via ChildNewtype::get which checks for None, and we hold a & reference to it continuously preventing it from being changed out from under us
let ptr_const = ptr as *const T;
unsafe { &*ptr_const } //safety, we create a reference with the same lifetime as Self, but keeping Self keeps the original data alive via the Drop implementation
}
}
impl<T> DerefMut for ChildNewtypeWriteGuard<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
let ptr = self.data.clone().unwrap(); //unwrap will not panic because this is created only via ChildNewtype::get which checks for None, and we hold a & reference to it continuously preventing it from being changed out from under us
unsafe { &mut *ptr } //safety, we create a reference with the same lifetime as Self, but keeping Self keeps the original data alive via the Drop implementation
}
}