take_some/
lib.rs

1//! A simple library that provides a way to obtain *some* value from various collections.
2//!
3//! [![pipeline status](https://gitlab.com/mexus/take-some-rs/badges/master/pipeline.svg)](https://gitlab.com/mexus/take-some-rs/commits/master)
4//! [![crates.io](https://img.shields.io/crates/v/take-some.svg)](https://crates.io/crates/take-some)
5//! [![docs.rs](https://docs.rs/take-some/badge.svg)](https://docs.rs/take-some)
6//!
7//! [[Release docs]](https://docs.rs/take-some/)
8//!
9//! [[Master docs]](https://mexus.gitlab.io/take-some-rs/take_some/)
10//!
11//! Sometimes one simply needs to "take" an element from a collection, no matter which element
12//! will it be. If you are that one, please feel free to use the crate that aims to make your
13//! (yes, yours!) life a tiny bit easier.
14//!
15//! Now let's see how it works.
16//! Let's say you want to implement a hash map that is statically guaranteed by the rust's type
17//! system to be non-empy at all the times.
18//! The most straightforward way to do so is to declare it as a pair of an item and the rest of the
19//! items, like `struct NonEmptyHashMap<K, V, S> (((K, V), ::std::collections::HashMap<K, V,S>))`.
20//!
21//! So far, so good. You will obviously want to create some nice user API for it, and that will
22//! include an instantiation of you type with a normal `HashMap` from the standard library. And
23//! here shines the `take-some` crate!
24//!
25//! ```rust
26//! extern crate take_some;
27//!
28//! use std::collections::HashMap;
29//! use std::hash::{BuildHasher, Hash};
30//! use take_some::TakeSome;
31//!
32//! struct NonEmptyHashMap<K, V, S> {
33//!     first: (K, V),
34//!     rest: HashMap<K, V, S>,
35//! }
36//!
37//! impl<K, V, S> NonEmptyHashMap<K, V, S>
38//! where
39//!     K: Eq + Hash,
40//!     S: BuildHasher,
41//! {
42//!     fn from_std_hashmap(mut map: HashMap<K, V, S>) -> Option<Self> {
43//!         map.take_some()
44//!             .map(|first| NonEmptyHashMap { first, rest: map })
45//!     }
46//!
47//!     // An alternative implementation that takes advantage of the `TakeSome::split_at_some`
48//!     // method.
49//!     fn from_std_hashmap2(map: HashMap<K, V, S>) -> Option<Self> {
50//!         map.split_at_some()
51//!             .map(|(first, rest)| NonEmptyHashMap { first, rest })
52//!             .ok()
53//!     }
54//! }
55//!
56//! fn main() {
57//!     let map: HashMap<String, String> = vec![
58//!         ("first".into(), "lol".into()),
59//!         ("second".into(), "lul".into()),
60//!     ].into_iter()
61//!         .collect();
62//!     let non_empty_map = NonEmptyHashMap::from_std_hashmap(map);
63//!     assert!(non_empty_map.is_some());
64//!
65//!     let empty_map: HashMap<String, String> = HashMap::new();
66//!     let non_empty_map = NonEmptyHashMap::from_std_hashmap(empty_map);
67//!     // It is entirely impossible (hail to the type system!) to create an instance of the
68//!     // `NonEmptyHashMap` with an empty map!
69//!     assert!(non_empty_map.is_none());
70//! }
71//! ```
72//!
73//! And that's it! Yes, it is that simple.
74//!
75//! If you'd like to implement the trait for your custom collections, have a look at the source
76//! code of the crate (`btree_map.rs`, `hash_set.rs`, `vec.rs` and so forth), or take a look at an
77//! example in the `examples` directory where we implement the trait for a "foreign" type.
78
79#![deny(missing_docs)]
80
81mod btree_map;
82mod btree_set;
83mod hash_map;
84mod hash_set;
85mod vec;
86
87/// A helper trait that allows to take *some* value out of a collection, i.e. remove a single
88/// element and return it, while preserving all others.
89pub trait TakeSome {
90    /// An item of the collection.
91    type Item;
92
93    /// Takes "some" element from a collection while preserving all others.
94    ///
95    /// In general case it could not be defined which exactly element will be returned (first,
96    /// last, greatest, least, ...), so, I repeat, in general case it is better to make no
97    /// assumptions on the ordering or anything else.
98    ///
99    /// There are several assumptions that trait implementors should take care of:
100    ///
101    /// * [`None`] should be returned if and only if the collection is empty.
102    /// * If the collection is NOT empty, exactly ONE element should be taken out from it, i.e.
103    ///   all the rest items should remains there.
104    /// * In case the collection IS empty, this call should NOT modify the collection.
105    /// * The implementors are encouraged to implement the method in a way that it will cause the
106    ///   least overhead possible.
107    fn take_some(&mut self) -> Option<Self::Item>;
108
109    /// Splits a given collection at "some" element, and returns a pair of that element and the
110    /// remaining collection, if "some" element was found, or returns the original collection if
111    /// "some" element could not be found. The latter happens if the collection is empty.
112    fn split_at_some(mut self) -> Result<(Self::Item, Self), Self>
113    where
114        Self: Sized,
115    {
116        match self.take_some() {
117            Some(x) => Ok((x, self)),
118            None => Err(self),
119        }
120    }
121}