stdext/option.rs
1//! Extension traits for `std::Option`.
2
3/// Extension trait with useful methods for [`std::option::Option`].
4///
5/// [`std::time::Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
6pub trait OptionExt<T> {
7 /// Combines `self` and another `Option`.
8 ///
9 /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some((s, o))`.
10 /// Otherwise, `None` is returned.
11 ///
12 /// **Note:** `std::Option` already provides a [`zip`] method which serves exact same purpose,
13 /// but currently it's unstable. Once it's stabilized, this method will be marked as deprecated
14 /// with a prompt to use the stanard method instead.
15 ///
16 /// # Examples
17 ///
18 /// ```
19 /// use stdext::prelude::*;
20 ///
21 /// let x = Some(1);
22 /// let y = Some("hi");
23 /// let z = None::<u8>;
24 ///
25 /// assert_eq!(x.combine(y), Some((1, "hi")));
26 /// assert_eq!(x.combine(z), None);
27 /// ```
28 ///
29 /// [`zip`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.zip
30 fn combine<U>(self, other: Option<U>) -> Option<(T, U)>;
31
32 /// Combines `self` and another `Option` with function `f`.
33 ///
34 /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some(f(s, o))`.
35 /// Otherwise, `None` is returned.
36 ///
37 /// **Note:** `std::Option` already provides a [`zip_with`] method which serves exact same purpose,
38 /// but currently it's unstable. Once it's stabilized, this method will be marked as deprecated
39 /// with a prompt to use the stanard method instead.
40 ///
41 /// # Examples
42 ///
43 /// ```
44 /// use stdext::prelude::*;
45 ///
46 /// #[derive(Debug, PartialEq)]
47 /// struct Point {
48 /// x: f64,
49 /// y: f64,
50 /// }
51 ///
52 /// impl Point {
53 /// fn new(x: f64, y: f64) -> Self {
54 /// Self { x, y }
55 /// }
56 /// }
57 ///
58 /// let x = Some(17.5);
59 /// let y = Some(42.7);
60 ///
61 /// assert_eq!(x.combine_with(y, Point::new), Some(Point { x: 17.5, y: 42.7 }));
62 /// assert_eq!(x.combine_with(None, Point::new), None);
63 /// ```
64 ///
65 /// [`zip_with`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.zip_with
66 fn combine_with<U, F, R>(self, other: Option<U>, f: F) -> Option<R>
67 where
68 F: FnOnce(T, U) -> R;
69}
70
71impl<T> OptionExt<T> for Option<T> {
72 fn combine<U>(self, other: Option<U>) -> Option<(T, U)> {
73 match (self, other) {
74 (Some(left), Some(right)) => Some((left, right)),
75 _ => None,
76 }
77 }
78
79 fn combine_with<U, F, R>(self, other: Option<U>, f: F) -> Option<R>
80 where
81 F: FnOnce(T, U) -> R,
82 {
83 self.combine(other).map(|(l, r)| f(l, r))
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn combine() {
93 // Test vector of (left, right, expected) values.
94 let test_vector = vec![
95 (Some(1), Some(2), Some((1, 2))),
96 (Some(1), None, None),
97 (None, Some(2), None),
98 (None, None, None),
99 ];
100
101 for (left, right, expected) in test_vector {
102 assert_eq!(left.combine(right), expected);
103 }
104 }
105
106 #[test]
107 fn combine_with() {
108 fn f(l: i32, r: i32) -> i32 {
109 l + r
110 }
111
112 // Test vector of (left, right, expected) values.
113 let test_vector = vec![
114 (Some(1), Some(2), Some(3)),
115 (Some(1), None, None),
116 (None, Some(2), None),
117 (None, None, None),
118 ];
119
120 for (left, right, expected) in test_vector {
121 assert_eq!(left.combine_with(right, f), expected);
122 }
123 }
124}