Hello Everyone,
This is my implementation for TrashPool, which is an object into which you can shove ownership of any temporary objects you want, and hang onto a reference, and they will live for the life of the TrashPool. It is conceptually along the same vein as ObjectiveC's (and Swift's and I'm sure other languages') AutoReleasePool.
I made it to specifically address the ugliness here: Help improving ergonomics
The unsafe
coercion effectively destroys any knowledge of the internal references, which I believe is ok in this case because nothing can leave the TrashPool. It's my first foray into unsafe
code though, so I may have missed something important. I did notice that dyn object references were 128 bits under the hood... so I'm basically relying on the first 64 bits of that being an ordinary reference. I figure, as long as the code that is putting the ownership into the TrashPool is safe, the right thing will happen... But I might be missing something big.
If the concept is sound, improvements could be made to improve allocation efficiency, etc.
Anyway, here's the implementation:
impl<T: ?Sized> Trashable for T {}
pub trait Trashable {}
pub struct TrashPool<'a> {
trash_vec : Vec<Box<dyn Trashable + 'a>>
}
impl <'a>TrashPool<'a> {
pub fn new() -> TrashPool<'a> {
TrashPool{trash_vec : vec![]}
}
pub fn dump<T:Trashable + 'a>(&'_ mut self, input : T) -> &'a T {
self.trash_vec.push(Box::new(input));
let trash_ref = &**self.trash_vec.last().unwrap();
unsafe { &*(trash_ref as *const dyn Trashable as *const T) }
}
}
And here is an example usage:
use std::cell::{RefCell};
pub struct Container {
data : RefCell<RefCell<Vec<u32>>>
}
impl Container {
pub fn borrow_iterator<'b, 'a : 'b>(&'a self, trash_pool : &'b mut TrashPool<'a>) -> impl Iterator<Item=&'b u32> {
let inner_cell_ref = trash_pool.dump(self.data.borrow());
let vec_ref = trash_pool.dump(inner_cell_ref.borrow());
vec_ref.iter()
}
}
fn main() {
let container = Container{data : RefCell::new(RefCell::new(vec![1u32; 2]))};
let mut trash_pool = TrashPool::new();
let iter = container.borrow_iterator(&mut trash_pool);
for value in iter {
println!("{}", value);
}
}
Maybe I'm heavily under the influence of the Ikea effect (IKEA effect - Wikipedia), but I feel like this is a reasonably nice solution to a whole class of problems that have been plaguing my use of Rust from the get-go. I understand it will always be better to structure your functions not to require a trash pool, but at least all the ugliness is contained in situations where there is no choice.
Thanks everyone for all your help and hand-holding over the past few days.