# 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`, and using recycled items to "recreate" owned copies of referenced items.
```rust
pub trait Recycler : Default {
type Item;
fn recycle(&mut self, item: Self::Item);
fn recreate(&mut self, other: &Self::Item) -> Self::Item;
}
```
The default `TrashRecycler` just drops arguments to `recycle` and clones arguments to `recreate`. However, smarter recyclers for types with owned memory can deconstruct the item and stash any of its owned memory, and then use the stashed memory to recreate items.
For example, `VecRecycler<R>` does just this for vectors of recycleable types:
```rust
// A recycler for vectors and their contents
pub struct VecRecycler<R: Recycler> {
pub recycler: R,
stash: Vec<Vec<R::Item>>,
}
impl<R: Recycler> Recycler for VecRecycler<R> {
type Item = Vec<R::Item>;
// recycles vec contents and then stashes the vec
fn recycle(&mut self, mut vec: Vec<R::Item>) {
while let Some(x) = vec.pop() {
self.recycler.recycle(x)
}
self.stash.push(vec);
}
// pops a stashed vector and then recreates each element
fn recreate(&mut self, other: &Vec<R::Item>) -> Vec<R::Item> {
let mut vec = self.stash.pop().unwrap_or(Vec::new());
for elem in other.iter() {
vec.push(self.recycler.recreate(elem));
}
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
```rust
#[bench]
fn allocate_vec_vec_str(bencher: &mut Bencher) {
bencher.iter(|| {
let mut v1 = Vec::with_capacity(10);
for _ in 0..10 {
let mut v2 = Vec::with_capacity(10);
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):
```rust
#[bench]
fn recycler_vec_vec_str(bencher: &mut Bencher) {
let mut r1 = make_recycler::<Vec<Vec<String>>>();
bencher.iter(|| {
let v = { // scope the borrow of r1
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);
});
}
```
The reason you do this is because if you run those benchmarks up there, you see numbers like:
test allocate_vec_vec_str ... bench: 3,494 ns/iter (+/- 1,128)
test recycler_vec_vec_str ... bench: 1,709 ns/iter (+/- 643)
If you do less formatting stuff and just put some `u64` data in the vectors, you see similar distinction:
test allocate_vec_vec_u64 ... bench: 267 ns/iter (+/- 49)
test recycler_vec_vec_u64 ... bench: 145 ns/iter (+/- 26)
The main down side is that you may get vectors that may have more memory than you need, and memory may also live for quite 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.
Note: a previous version of these numbers looked worse for the `allocate` variants because they used `Vec::new()` rather than `Vec::with_capacity(10)`, which correctly sizes the allocation and avoids copies.
## recreate
If for some reason you find you are often given references to objects and need a quick clone (for example, using `decode` or `verify` in [Abomonation](https://github.com/frankmcsherry/abomonation)), the `recreate` method is meant to be painless. The above benchmark becomes:
```rust
#[bench]
fn recreate_vec_vec_str(bencher: &mut Bencher) {
let mut recycler = make_recycler::<Vec<Vec<String>>>();
let data = vec![vec!["test!".to_owned(); 10]; 10];
bencher.iter(|| {
let record = recycler.recreate(&data);
recycler.recycle(record);
});
}
```
If you compare using `recreate` with just using `clone`, you see numbers like:
test clone_vec_vec_str ... bench: 2,906 ns/iter (+/- 774)
test recreate_vec_vec_str ... bench: 1,773 ns/iter (+/- 625)
test clone_vec_vec_u64 ... bench: 344 ns/iter (+/- 134)
test recreate_vec_vec_u64 ... bench: 157 ns/iter (+/- 42)
## thanks!
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.