recycler 0.1.1

A small Rust library for recycling types containing owned memory
Documentation

recycler

A small Rust library for recycling types with owned memory

Recycler provides the Recycler trait and several implementations. Each Recycler object is capable of "recycling" items of its associated type Item.

// allows recycling of items
pub trait Recycler : Default {
    type Item;
    fn recycle(&mut self, _item: Self::Item) { }
}

The intended behavior is that types with owned memory will be deconstructed and have the owned memory enqueued. For example, the implementation for VecRecycler<R> is just

// recycles vec contents, then stashes the vec
impl<R: Recycler> Recycler for VecRecycler<R> {
    type Item = Vec<R::Item>;
    fn recycle(&mut self, mut vec: Vec<R::Item>) {
        while let Some(x) = vec.pop() {
            self.recycler.recycle(x)
        }
        self.stash.push(vec);
    }
}

While recycling might sound great just because of civic duty, the real purpose is that these recyclers are able to return the owned memory to you, using a pattern not unlike standard allocation. Where you might write something like

#[bench]
fn allocate_vec_vec_str(bencher: &mut Bencher) {
    bencher.iter(|| {
        let mut v1 = Vec::new();
        for _ in 0..10 {
            let mut v2 = Vec::new();
            for _ in 0..10 {
                v2.push(("test!").to_owned());
            }
            v1.push(v2);
        }
        v1
    });
}

you can now instead write something pretty similar (no, not the same):

#[bench]
fn recycler_vec_vec_str(bencher: &mut Bencher) {
    let mut r1: VecRecycler<VecRecycler<StringRecycler>> = Default::default();
    bencher.iter(|| {
        let v = {
            let (mut v1, r2) = r1.new();
            for _ in 0..10 {
                let (mut v2, r3) = r2.new();
                for _ in 0..10 {
                    v2.push(r3.new_from("test!"));
                }
                v1.push(v2);
            }
            v1
        };
        r1.recycle(v);
    });
}

There is a bit of nonsense because of lexical borrows, and because type inference doesn't seem to do a great job finding an implementation with an associated type as a constraint. Perhaps I'll learn that this was a bad pattern.

The reason you do this is because if you run those benchmarks up there, you see numbers like:

test allocate_vec_vec_str   ... bench:      4556 ns/iter (+/- 2166)
test recycler_vec_vec_str   ... bench:      2033 ns/iter (+/- 945)

If you do less formatting stuff and just put some u64 data in the vectors, you see an even bigger distinction:

test allocate_vec_vec_u64   ... bench:      1281 ns/iter (+/- 303)
test recycler_vec_vec_u64   ... bench:       154 ns/iter (+/- 27)

The main down side I can think of is that you are getting vectors that may have more memory than you need. They may also live for a while in the recycler. I almost added a clear method, but if you want to do that just make a new recycler and clobber the old one.

If anyone has any hot tips or recommendations, especially about a macro or syntax extension that would let structs and such automatically derive recyclers, I'd be all ears. Any other friendly comments or contributions are also welcome.