[−][src]Crate safe_index
Strongly-typed, zero-cost indexes wrapping integers.
This crate is just one macro: new
. It creates a wrapper around usize
to make
type-safe indexes. That is, the indexes for your clients that you use to retrieve information
efficiently from the vector of client information do not have the same type as the indexes for
the files you have about your clients. The example below illustrates this crate in
that context.
The index type created implements
Deref
andFrom
forusize
,Debug
,Clone
,Copy
,PartialOrd
,Ord
,PartialEq
,Eq
,Hash
andDisplay
.
Usage
The most basic use of new
is just to wrap something:
safe_index::new!{ /// Arity. Arity } assert_eq! { std::mem::size_of::<Arity>(), std::mem::size_of::<usize>() }
This is not very useful however, there's nothing for our index to index. Thankfully new
can provide more types. After the mandatory identifier Idx
for the type of indexes, you can
add these:
range <Range>
: creates an iterator named<Range>
between twoIdx
s (the upper bound is exclusive). If this constructor is present,Idx
will have aup_to
function that creates a range between twoIdx
s. This constructor can only appear once.map <Map> with iter: <MapIter>
: creates a wrapper named<Map>
around a vector, indexed byIdx
.<MapIter>
is the type of iterators over<Map>
.btree set <Set>
: alias type for a binary tree set ofIdx
s.btree map <Map>
: alias type for a binary tree map fromIdx
to something.
See the examples
module and the example below for illustrations of the new
macro.
Example
All the code for this example is in examples::clients
. Say we have a Data
structure that
stores some clients in a vector. It also stores files about these clients. A client can be
associated to several files, and a file can be about several clients. Let's handle everything
by indexes:
/// Client information. pub struct ClientInfo { /// Name of the client. pub name: String, /// Indices of files associated with the client. pub files: BTreeSet<usize>, } /// File information. pub struct FileInfo { /// Name of the file. pub name: String, /// Indices of clients concerned by the file. pub clients: BTreeSet<usize>, } /// Aggregates clients and files info. pub struct Data { /// Map from client indexes to client information. pub clients: Vec<ClientInfo>, /// Map from file indexes to file information. pub files: Vec<FileInfo>, }
Now, implementing Data
's functionalities is going to be painful. Client and file indexes are
both usize
, terrible things are bound to happen.
So let's instead create an index type for each.
/// Indices. pub mod idx { safe_index::new! { /// Indices of clients. Client, /// Map from clients to something (really a vector). map: Clients with iter: ClientIter, /// Set of clients. btree set: ClientSet, } safe_index::new! { /// Indices of files. File, /// Map from files to something (really a vector). map: Files with iter: FileIter, /// Set of files. btree set: FileSet, } } use idx::*; /// Client information. pub struct ClientInfo { /// Name of the client. pub name: String, /// Indices of files associated with the client. pub files: ClientSet, } /// File information. pub struct FileInfo { /// Name of the file. pub name: String, /// Indices of clients concerned by the file. pub clients: FileSet, } /// Aggregates clients and files info. pub struct Data { /// Map from client indexes to client information. pub clients: Clients<ClientInfo>, /// Map from file indexes to file information. pub files: Files<FileInfo>, }
The full code is available here, and you can see it used in the documentation of
examples::clients
. Here are a few functions on Data
to (hopefully) show that Client
and
File
behave as (and in fact are) usize
indexes.
/// Aggregates clients and files info. pub struct Data { /// Map from client indexes to client information. pub clients: Clients<ClientInfo>, /// Map from file indexes to file information. pub files: Files<FileInfo>, } impl Data { /// Adds a file, updates the clients concerned. pub fn add_file(&mut self, file: FileInfo) -> File { let idx = self.files.next_index(); for client in &file.clients { let is_new = self.clients[*client].files.insert(idx); debug_assert! { is_new } } let nu_idx = self.files.push(file); debug_assert_eq! { idx, nu_idx } idx } /// Adds a client to a file. pub fn add_client_to_file(&mut self, client: Client, file: File) { let is_new = self.files[file].clients.insert(client); debug_assert! { is_new } let is_new = self.clients[client].files.insert(file); debug_assert! { is_new } } }
Modules
examples | Examples of using |
Macros
new | Wraps a |