I'm currently implementing a kind of smart pointer that allows to track dropping of an entity.
It's intended to track changes made to entities loaded from db and only write back changes (including entities that were removed from a collection, without doing a bunch).
Currently I'm using a Option<Weak<dyn Tracker> to track deletion on drop.
With that design entities are able to outlive the db session.
I would like to use lifetimes to ensure that entities do not outlive the session without explicitly being detached.
The std library provides std::intrinsics::transmute, which can be used to extend or shorten the lifetime accordingly.
An implementation could look like this.
I've commented an alternative solution only using safe Rust.
The safe alternative has the problem that it needs to drop the original self, since Tracked<'t, E>
and Tracked<'static, E>
are two different types, but i feel like the "unsafe" variant should be safe?
use std::sync::Arc;
pub trait Tracker<E> {
fn track(&self, entity: Arc<E>);
}
pub struct Tracked<'t, E> {
inner: Arc<E>,
tracker: Option<&'t dyn Tracker<E>>
}
impl<'t, E> Drop for Tracked<'t, E> {
fn drop(&mut self) {
if let Some(tracker) = self.tracker.as_ref() {
tracker.track(self.inner.clone());
}
}
}
impl<'a, E> Tracked<'a, E> {
pub fn register<'t>(mut self, tracker: &'t dyn Tracker<E>) -> Tracked<'t,E> {
let mut s: Tracked<'t, E> = unsafe{ std::mem::transmute(self)};
s.tracker = Some(tracker);
s
// self.tracker = None;
// Tracked {
// inner: self.inner.clone(),
// tracker: Some(tracker)
// }
}
pub fn deregister(mut self) -> Tracked<'static,E> {
let mut s: Tracked<'static,E> = unsafe{ std::mem::transmute(self)};
s.tracker = None;
s
// self.tracker = None;
// Tracked {
// inner: self.inner.clone(),
// tracker: None
// }
}
pub fn new(inner: E) -> Self{
Self {
inner: Arc::new(inner),
tracker: None
}
}
}
pub struct NoopTracker {}
impl<E> Tracker<E> for NoopTracker {
fn track(&self, inner: Arc<E>) {}
}
pub fn main() {
let a = 0u8;
let track = Tracked::new(a);
let tracker = NoopTracker{};
let track = track.register( &tracker);
// drop(tracker); // borrow checker error here, as expected
let track = track.deregister();
drop(tracker);
}