take_if/lib.rs
1//! Conditionally take a value out of an option.
2//!
3//! This crate adds a `take_if` extension method to [`Option`] which conditionally
4//! takes the value out of an option, leaving `None` in its place if the value was
5//! taken. The predicate function is only called if the option is `Some`, and
6//! receives a reference to the option's contents.
7//!
8//! If you don't need to take the value conditionally, i.e. you always need to take
9//! the value, use [`Option::take`] instead.
10//!
11//! # Examples
12//!
13//! ```
14//! use take_if::TakeIf;
15//!
16//! let mut maybe_greeting = Some("Hello, World!");
17//!
18//! if let Some(greeting) = maybe_greeting.take_if(|greeting| greeting.starts_with("Hello")) {
19//! println!(r#"Greeting {:?} starts with "Hello""#, greeting);
20//! } else {
21//! println!(r#"There was no greeting, or it didn't start with "Hello""#);
22//! }
23//! ```
24//!
25//! [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
26//! [`Option::take`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.take
27
28/// Extension trait for `Option<T>` that adds the `take_if` method.
29///
30/// See the [crate-level documentation](./index.html) for more information.
31pub trait TakeIf {
32 /// The type contained in the `Option`.
33 type Inner;
34
35 /// Takes value out of the `Option` if `predicate` returns `true`.
36 ///
37 /// See the [crate-level documentation](./index.html) for more information.
38 fn take_if<F: FnOnce(&Self::Inner) -> bool>(&mut self, predicate: F) -> Option<Self::Inner>;
39}
40
41impl<T> TakeIf for Option<T> {
42 type Inner = T;
43
44 fn take_if<F: FnOnce(&Self::Inner) -> bool>(&mut self, predicate: F) -> Option<Self::Inner> {
45 if self.as_ref().map(predicate).unwrap_or(false) {
46 self.take()
47 } else {
48 None
49 }
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use crate::TakeIf;
56
57 #[test]
58 fn conditional_take() {
59 let mut option = Some(5);
60 assert_eq!(None, option.take_if(|_| false));
61 assert_eq!(Some(5), option.take_if(|_| true));
62 assert_eq!(None, option.take_if(|_| true));
63 }
64}