Crate coatcheck [-] [src]
CoatCheck
CoatCheck is a library for storing values and referencing them by "handles" (Tickets
). This
library is designed to be used where you would otherwise use a hash table but you don't
actually need to be able to choose the keys.
Advantages over a hash table:
- You don't have to generate your keys.
- CoatCheck is at least 5x faster than the stdlib HashTable for insert/remove operations.
- CoatCheck is about 40x faster for lookup operations.
Example
use coatcheck::{CoatCheck, Ticket}; use std::convert::From; let mut cc = CoatCheck::new(); // Check two values. let ticket1 = cc.check("my value"); let ticket2 = cc.check("my other value"); // Look at the first one. println!("{}", cc[&ticket1]); // Claim the second one. println!("{}", cc.claim(ticket2).unwrap()); // Claiming again will fail at compile time. // println!("{}", cc.claim(ticket2).unwrap()); // Drain the items into a vector. let items: Vec<&str> = cc.into_iter().collect(); assert_eq!(items[0], "my value"); // Create a second coat check: let mut cc2: CoatCheck<&str> = CoatCheck::new(); // `ticket1` was never claimed so let's try claiming it in this coat check... let ticket: Ticket = From::from(cc2.claim(ticket1).unwrap_err()); // It fails and returns the ticket.
Use Case
For example, let's say you were implementing a callback system:
struct System { callbacks: Vec<Box<FnMut() + 'static>>, } impl System { fn add_callback<C>(&mut self, cb: C) where C: FnMut() + 'static { self.callbacks.push(Box::new(cb)); } fn fire(&mut self) { for cb in self.callbacks.iter_mut() { (cb)(); } } }
This system works but doesn't allow unregistering. If you wanted to allow unregistering individual callbacks, you could do something like:
use std::collections::HashMap; struct System { callbacks: HashMap<usize, Box<FnMut() + 'static>>, next_id: usize, } struct Handle { id: usize, } impl System { fn add_callback<C>(&mut self, cb: C) -> Handle where C: FnMut() + 'static { let id = self.next_id; self.next_id += 1; self.callbacks.insert(id, Box::new(cb)); Handle { id: id } } fn fire(&mut self) { for (_, cb) in self.callbacks.iter_mut() { (cb)(); } } fn remove_callback(&mut self, handle: Handle) { self.callbacks.remove(&handle.id); } }
However, we don't REALLY need a hash table because we don't care about the keys.
This is where this library comes in. It acts like the above system but takes advantage of the fact that it can choose the IDs:
use coatcheck::{CoatCheck, Ticket}; struct System { callbacks: CoatCheck<Box<FnMut() + 'static>>, } struct Handle { ticket: Ticket, // Wrap it for type safety } impl System { fn add_callback<C>(&mut self, cb: C) -> Handle where C: FnMut() + 'static { Handle { ticket: self.callbacks.check(Box::new(cb)) } } fn remove_callback(&mut self, handle: Handle) { self.callbacks.claim(handle.ticket); } fn fire(&mut self) { for cb in self.callbacks.iter_mut() { (*cb)(); } } }
Discussion
One thing you might note when using this library is that Tickets can't be duplicated in any way.
Pros:
Ownership: Preventing duplication of the ticket preserves ownership of the value to an extent. The value can still be stolen by destroying the CoatCheck but that's the only way to get it out without the ticket.
Safety: As long as you use the ticket in the right coat check, the index operator will never panic.
Size: If I allowed ticket copying, I'd need to store a "generation" in every ticket and along side the ticket's associated value to be able to distinguish between an old ticket and a new one. Currently, I can get away with reusing tickets because they must be turned in before freeing a slot.
Cons:
- Multiple references: There's no way to give away a reference to a value (without using actual references, that is).
Structs
AccessError |
The error yielded an access fails. |
ClaimError |
The error yielded when a claim fails. |
CoatCheck |
A data structure storing values indexed by tickets. |
Ticket |
A |
Tickets |
Iterator that checks-in values in exchange for tickets. |
Enums
ErrorKind |
Coat check error types |