rpds/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(feature = "fatal-warnings", deny(warnings))]
3// Note: If you change this remember to update `README.md`. To do so run `cargo rdme`.
4//! Rust Persistent Data Structures provides [fully persistent data structures](https://en.wikipedia.org/wiki/Persistent_data_structure)
5//! with structural sharing.
6//!
7//! # Setup
8//!
9//! To use rpds add the following to your `Cargo.toml`:
10//!
11//! ```toml
12//! [dependencies]
13//! rpds = "<version>"
14//! ```
15//!
16//! # Data structures
17//!
18//! This crate offers the following data structures:
19//!
20//! 1. [`List`](#list)
21//! 2. [`Vector`](#vector)
22//! 3. [`Stack`](#stack)
23//! 4. [`Queue`](#queue)
24//! 5. [`HashTrieMap`](#hashtriemap)
25//! 6. [`HashTrieSet`](#hashtrieset)
26//! 7. [`RedBlackTreeMap`](#redblacktreemap)
27//! 8. [`RedBlackTreeSet`](#redblacktreeset)
28//!
29//! ## `List`
30//! [](crate::list::List)
31//!
32//! Your classic functional list.
33//!
34//! ### Example
35//!
36//! ```rust
37//! use rpds::List;
38//!
39//! let list = List::new().push_front("list");
40//!
41//! assert_eq!(list.first(), Some(&"list"));
42//!
43//! let a_list = list.push_front("a");
44//!
45//! assert_eq!(a_list.first(), Some(&"a"));
46//!
47//! let list_dropped = a_list.drop_first().unwrap();
48//!
49//! assert_eq!(list_dropped, list);
50//! ```
51//!
52//! ## `Vector`
53//! [](crate::vector::Vector)
54//!
55//! A sequence that can be indexed. The implementation is described in
56//! [Understanding Persistent Vector Part 1](http://hypirion.com/musings/understanding-persistent-vector-pt-1)
57//! and [Understanding Persistent Vector Part 2](http://hypirion.com/musings/understanding-persistent-vector-pt-2).
58//!
59//! ### Example
60//!
61//! ```rust
62//! use rpds::Vector;
63//!
64//! let vector = Vector::new()
65//! .push_back("Iām")
66//! .push_back("a")
67//! .push_back("vector");
68//!
69//! assert_eq!(vector[1], "a");
70//!
71//! let screaming_vector = vector
72//! .drop_last().unwrap()
73//! .push_back("VECTOR!!!");
74//!
75//! assert_eq!(screaming_vector[2], "VECTOR!!!");
76//! ```
77//!
78//! ## `Stack`
79//! [](crate::stack::Stack)
80//!
81//! A LIFO (last in, first out) data structure. This is just a [`List`](#list) in disguise.
82//!
83//! ### Example
84//!
85//! ```rust
86//! use rpds::Stack;
87//!
88//! let stack = Stack::new().push("stack");
89//!
90//! assert_eq!(stack.peek(), Some(&"stack"));
91//!
92//! let a_stack = stack.push("a");
93//!
94//! assert_eq!(a_stack.peek(), Some(&"a"));
95//!
96//! let stack_popped = a_stack.pop().unwrap();
97//!
98//! assert_eq!(stack_popped, stack);
99//! ```
100//!
101//! ## `Queue`
102//! [](crate::queue::Queue)
103//!
104//! A FIFO (first in, first out) data structure.
105//!
106//! ### Example
107//!
108//! ```rust
109//! use rpds::Queue;
110//!
111//! let queue = Queue::new()
112//! .enqueue("um")
113//! .enqueue("dois")
114//! .enqueue("tres");
115//!
116//! assert_eq!(queue.peek(), Some(&"um"));
117//!
118//! let queue_dequeued = queue.dequeue().unwrap();
119//!
120//! assert_eq!(queue_dequeued.peek(), Some(&"dois"));
121//! ```
122//!
123//! ## `HashTrieMap`
124//! [](crate::map::hash_trie_map::HashTrieMap)
125//!
126//! A map implemented with a [hash array mapped trie](https://en.wikipedia.org/wiki/Hash_array_mapped_trie).
127//! See [Ideal Hash Trees](https://infoscience.epfl.ch/record/64398/files/idealhashtrees.pdf) for
128//! details.
129//!
130//! ### Example
131//!
132//! ```rust
133//! use rpds::HashTrieMap;
134//!
135//! let map_en = HashTrieMap::new()
136//! .insert(0, "zero")
137//! .insert(1, "one");
138//!
139//! assert_eq!(map_en.get(&1), Some(&"one"));
140//!
141//! let map_pt = map_en
142//! .insert(1, "um")
143//! .insert(2, "dois");
144//!
145//! assert_eq!(map_pt.get(&2), Some(&"dois"));
146//!
147//! let map_pt_binary = map_pt.remove(&2);
148//!
149//! assert_eq!(map_pt_binary.get(&2), None);
150//! ```
151//!
152//! ## `HashTrieSet`
153//! [](crate::set::hash_trie_set::HashTrieSet)
154//!
155//! A set implemented with a [`HashTrieMap`](#hashtriemap).
156//!
157//! ### Example
158//!
159//! ```rust
160//! use rpds::HashTrieSet;
161//!
162//! let set = HashTrieSet::new()
163//! .insert("zero")
164//! .insert("one");
165//!
166//! assert!(set.contains(&"one"));
167//!
168//! let set_extended = set.insert("two");
169//!
170//! assert!(set_extended.contains(&"two"));
171//!
172//! let set_positive = set_extended.remove(&"zero");
173//!
174//! assert!(!set_positive.contains(&"zero"));
175//! ```
176//!
177//! ## `RedBlackTreeMap`
178//! [](crate::map::red_black_tree_map::RedBlackTreeMap)
179//!
180//! A map implemented with a [red-black tree](https://en.wikipedia.org/wiki/Red-Black_tree).
181//!
182//! ### Example
183//!
184//! ```rust
185//! use rpds::RedBlackTreeMap;
186//!
187//! let map_en = RedBlackTreeMap::new()
188//! .insert(0, "zero")
189//! .insert(1, "one");
190//!
191//! assert_eq!(map_en.get(&1), Some(&"one"));
192//!
193//! let map_pt = map_en
194//! .insert(1, "um")
195//! .insert(2, "dois");
196//!
197//! assert_eq!(map_pt.get(&2), Some(&"dois"));
198//!
199//! let map_pt_binary = map_pt.remove(&2);
200//!
201//! assert_eq!(map_pt_binary.get(&2), None);
202//!
203//! assert_eq!(map_pt_binary.first(), Some((&0, &"zero")));
204//! ```
205//!
206//! ## `RedBlackTreeSet`
207//! [](crate::set::red_black_tree_set::RedBlackTreeSet)
208//!
209//! A set implemented with a [`RedBlackTreeMap`](#redblacktreemap).
210//!
211//! ### Example
212//!
213//! ```rust
214//! use rpds::RedBlackTreeSet;
215//!
216//! let set = RedBlackTreeSet::new()
217//! .insert("zero")
218//! .insert("one");
219//!
220//! assert!(set.contains(&"one"));
221//!
222//! let set_extended = set.insert("two");
223//!
224//! assert!(set_extended.contains(&"two"));
225//!
226//! let set_positive = set_extended.remove(&"zero");
227//!
228//! assert!(!set_positive.contains(&"zero"));
229//!
230//! assert_eq!(set_positive.first(), Some(&"one"));
231//! ```
232//!
233//! # Other features
234//!
235//! ## Mutable methods
236//!
237//! When you change a data structure you often do not need its previous versions. For those cases
238//! rpds offers you mutable methods which are generally faster:
239//!
240//! ```rust
241//! use rpds::HashTrieSet;
242//!
243//! let mut set = HashTrieSet::new();
244//!
245//! set.insert_mut("zero");
246//! set.insert_mut("one");
247//!
248//! let set_0_1 = set.clone();
249//! let set_0_1_2 = set.insert("two");
250//! ```
251//!
252//! ## Initialization macros
253//!
254//! There are convenient initialization macros for all data structures:
255//!
256//! ```rust
257//! use rpds::*;
258//!
259//! let vector = vector![3, 1, 4, 1, 5];
260//! let map = ht_map!["orange" => "orange", "banana" => "yellow"];
261//! ```
262//!
263//! Check the documentation for initialization macros of other data structures.
264//!
265//! ## Thread safety
266//!
267//! All data structures in this crate can be shared between threads, but that is an opt-in ability.
268//! This is because there is a performance cost to make data structures thread safe. That cost
269//! is worth avoiding when you are not actually sharing them between threads.
270//!
271//! Of course if you try to share a rpds data structure across different threads you can count on
272//! the rust compiler to ensure that it is safe to do so. If you are using the version of the data
273//! structure that is not thread safe you will get a compile-time error.
274//!
275//! To create a thread-safe version of any data structure use `new_sync()`:
276//!
277//! ```rust
278//! # use rpds::Vector;
279//! #
280//! let vec = Vector::new_sync()
281//! .push_back(42);
282//! ```
283//!
284//! Or use the `_sync` variant of the initialization macro:
285//!
286//! ```rust
287//! # use rpds::vector_sync;
288//! #
289//! let vec = vector_sync!(42);
290//! ```
291//!
292//! ### Further details
293//!
294//! Internally the data structures in this crate maintain a lot of reference-counting pointers.
295//! These pointers are used both for links between the internal nodes of the data structure as well
296//! as for the values it stores.
297//!
298//! There are two implementations of reference-counting pointers in the standard library:
299//! [`Rc`](::alloc::rc::Rc) and
300//! [`Arc`](::alloc::sync::Arc). They behave the same way, but
301//! `Arc` allows you to share the data it points to across multiple threads. The downside is that
302//! it is significantly slower to clone and drop than `Rc`, and persistent data structures do a
303//! lot of those operations. In some microbenchmarks with rpds data structure we can see that
304//! using `Rc` instead of `Arc` can make some operations twice as fast! You can see this for
305//! yourself by running `cargo bench`.
306//!
307//! To implement this we parameterize the type of reference-counting pointer (`Rc` or `Arc`) as a
308//! type argument of the data structure. We use the [archery](https://github.com/orium/archery/)
309//! crate to do this in a convenient way.
310//!
311//! The pointer type can be parameterized like this:
312//!
313//! ```rust
314//! # use rpds::Vector;
315//! #
316//! let vec: Vector<u32, archery::ArcTK> = Vector::new_with_ptr_kind();
317//! // ā
318//! // This will use `Arc` pointers.
319//! // Change it to `archery::RcK` to use a `Rc` pointer.
320//! ```
321//!
322//! ## `no_std` support
323//!
324//! This crate supports `no_std`. To enable that you need to disable the default feature `std`:
325//!
326//! ```toml
327//! [dependencies]
328//! rpds = { version = "<version>", default-features = false }
329//! ```
330//!
331//! ## Serialization
332//!
333//! We support serialization through [serde](https://crates.io/crates/serde). To use it
334//! enable the `serde` feature. To do so change the rpds dependency in your `Cargo.toml` to
335//!
336//! ```toml
337//! [dependencies]
338//! rpds = { version = "<version>", features = ["serde"] }
339//! ```
340//!
341//! ## Bindings
342//!
343//! Bindings to use rpds from other programming languages exist. Below is a short list of those
344//! known to date.
345//!
346//! * [rpds.py](https://github.com/crate-py/rpds/) ā Python
347//!
348//! Please feel free to send a pull request should you add support in a new language.
349
350extern crate alloc;
351
352#[cfg(test)]
353#[macro_use]
354extern crate std;
355
356mod utils;
357#[macro_use]
358pub mod list;
359pub mod map;
360pub mod queue;
361pub mod set;
362pub mod stack;
363pub mod vector;
364
365pub use crate::list::List;
366pub use crate::list::ListSync;
367pub use crate::map::hash_trie_map::HashTrieMap;
368pub use crate::map::hash_trie_map::HashTrieMapSync;
369pub use crate::map::red_black_tree_map::RedBlackTreeMap;
370pub use crate::map::red_black_tree_map::RedBlackTreeMapSync;
371pub use crate::queue::Queue;
372pub use crate::queue::QueueSync;
373pub use crate::set::hash_trie_set::HashTrieSet;
374pub use crate::set::hash_trie_set::HashTrieSetSync;
375pub use crate::set::red_black_tree_set::RedBlackTreeSet;
376pub use crate::set::red_black_tree_set::RedBlackTreeSetSync;
377pub use crate::stack::Stack;
378pub use crate::stack::StackSync;
379pub use crate::vector::Vector;
380pub use crate::vector::VectorSync;