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
impl
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
fn allocatevecvecstr(bencher: &mut Bencher) { bencher.iter(|| { let mut v1 = Vec::withcapacity(10); for _ in 0..10 { let mut v2 = Vec::withcapacity(10); for _ in 0..10 { v2.push(("test!").toowned()); } v1.push(v2); } v1 }); } ```
you can now instead write something pretty similar (no, not the same):
```rust
fn recyclervecvecstr(bencher: &mut Bencher) {
let mut r1 = makerecycler::
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.
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), the recreate
method is meant to be painless. The above benchmark becomes:
```rust
fn recreatevecvecstr(bencher: &mut Bencher) {
let mut recycler = makerecycler::
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)
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.