Hey! I'd like to write a library that seamlessly transactionally persists mutations to things that are rustc_serialize::{Encodable, Decodable}. But I don't know if rust can do this, after hitting my head against the wall for a bit.
Ideally it would look sort of like lazy_static, but would wrap the declared serializable state in something that uses some combination of DerefMut and Drop trickiness to log mutations in a safe way that can be resumed after restarting a process. Not thinking about databases or serialization would speed up my projects so much.
Is this outside of the realm of what is possible with rust right now?
Hitting so many fundamental problems, I'm becoming doubtful, any help (or statement of impossibility) would be wonderful!
rough idea of how I would like to use this (final lib would wrap a struct over the desired value similar to lazy_static, and the user in this example would just think about u64's rather than any wrapping structs)
One problem you will run into is interior mutability (e.g., RefCell); you can mutate a data structure with interior mutability even if you don't have a mutable pointer to it. You can't even work around this by saving on Deref (immutable) because the data structure could have shared state, e.g. struct MyStruct(Rc<RefCell<u64>>).
Technically, you could fix this using OIBITs but that's unstable:
unsafe trait NoInterriorMutability {}
unsafe impl NoInterriorMutability for .. {}
impl<T: ?Sized> !NoInterriorMutability for UnsafeCell<T> {}
struct AutoSave<T: NoInterriorMutability>(T);
Note: Even this solution isn't perfect as the serialize/deserialize functions could read/modify global state (state stored in a static and protected by a mutex). However, I'd consider any serialization logic reading/writing global state to be a bug.
So, it may be possible but I'm not sure. Regardless, it looks like a cool project and I'd like to see if you can get it working.
This can't be made to work with DerefMut. The return value of DerefMut must be a reference, which means it must be a reference to an existing object. Within a function, you can write something like let x = &123; and it will 'magically' allocate a stack slot for 123 and keep it around until x goes out of scope; but that's a special case that doesn't work across functions. Thus it will never work to return &Box::new(foo).
You'd have to use an explicit method call instead of the implicit one triggered by DerefMut, like my_struct.grab().prop += 1;.