11 releases
0.10.0 | Oct 21, 2022 |
---|---|
0.9.20 | May 16, 2022 |
0.9.19 | Jan 3, 2022 |
0.9.17 | Nov 21, 2021 |
0.9.2 | Mar 4, 2019 |
#762 in Data structures
Used in ecore_rs
43KB
755 lines
safe_index
Zero-cost-wraps usize
-s to give them a specific type. The motivation is to have different kinds of
indices that are incompatible at type-level, thus lowering the chance of mixing them up compared to
using usize
-s.
Index-type creation is done through a macro, so the type actually belong to the client crate. This lets users augment index-types with methods, trait implementations, etc.
See the documentation for details.
If you are experiencing problems upgrading from a version < 0.9.17
, make sure you read the
changelog.
lib.rs
:
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
,Default
,Clone
,Copy
,PartialOrd
,Ord
,PartialEq
,Eq
,Hash
andDisplay
.
If you are experiencing problems upgrading from a version < 0.9.17
, make sure you read the
changelog.
Usage
The most basic use of new
is just to wrap something:
safe_index::new!{
/// Arity.
Arity
}
assert_eq! { core::mem::size_of::<Arity>(), core::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:
map <Map>
: creates a wrapper named<Map>
around a vector, indexed byIdx
.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,
/// Set of clients.
btree set: ClientSet,
}
safe_index::new! {
/// Indices of files.
File,
/// Map from files to something (really a vector).
map: Files,
/// 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.push(file);
let file = &self.files[idx];
for client in &file.clients {
let is_new = self.clients[*client].files.insert(idx);
debug_assert! { is_new }
}
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 }
}
}