blob: 6dd396201ded7d816a649c7de9935e8dc204fad7 [file] [log] [blame]
Paul Crowley7a658392021-03-18 17:08:20 -07001// Copyright 2021, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The TryInsert trait adds to Option<T> the method
16//! get_or_try_to_insert_with, which is analogous to
17//! get_or_insert_with, but allows the called function to fail and propagates the failure.
18
19/// The TryInsert trait adds to Option<T> the method
20/// get_or_try_to_insert_with, which is analogous to
21/// get_or_insert_with, but allows the called function to fail and propagates the failure.
22pub trait TryInsert {
23 /// Type of the Ok branch of the Result
24 type Item;
25 /// Inserts a value computed from `f` into the option if it is [`None`],
26 /// then returns a mutable reference to the contained value. If `f`
27 /// returns Err, the Option is unchanged.
28 ///
29 /// # Examples
30 ///
31 /// ```
32 /// let mut x = None;
33 /// assert_eq!(x.get_or_try_to_insert_with(Err("oops".to_string())), Err("oops".to_string()))
34 /// {
35 /// let y: &mut u32 = x.get_or_try_to_insert_with(|| Ok(5))?;
36 /// assert_eq!(y, &5);
37 ///
38 /// *y = 7;
39 /// }
40 ///
41 /// assert_eq!(x, Some(7));
42 /// ```
43 fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
44 &mut self,
45 f: F,
46 ) -> Result<&mut Self::Item, E>;
47}
48
49impl<T> TryInsert for Option<T> {
50 type Item = T;
51 fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
52 &mut self,
53 f: F,
54 ) -> Result<&mut Self::Item, E> {
55 if self.is_none() {
56 *self = Some(f()?);
57 }
58
59 match self {
60 Some(v) => Ok(v),
61 // SAFETY: a `None` variant for `self` would have been replaced by a `Some`
62 // variant in the code above.
63 None => unsafe { std::hint::unreachable_unchecked() },
64 }
65 }
66}
67
68#[cfg(test)]
69mod test {
70 use super::*;
71
72 fn fails() -> Result<i32, String> {
73 Err("fail".to_string())
74 }
75
76 fn succeeds() -> Result<i32, String> {
77 Ok(99)
78 }
79
80 #[test]
81 fn test() {
82 let mut x = None;
83 assert_eq!(x.get_or_try_to_insert_with(fails), Err("fail".to_string()));
84 assert_eq!(x, None);
85 assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 99);
86 assert_eq!(x, Some(99));
87 x = Some(42);
88 assert_eq!(*x.get_or_try_to_insert_with(fails).unwrap(), 42);
89 assert_eq!(x, Some(42));
90 assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 42);
91 assert_eq!(x, Some(42));
92 *x.get_or_try_to_insert_with(fails).unwrap() = 2;
93 assert_eq!(x, Some(2));
94 *x.get_or_try_to_insert_with(succeeds).unwrap() = 3;
95 assert_eq!(x, Some(3));
96 x = None;
97 *x.get_or_try_to_insert_with(succeeds).unwrap() = 5;
98 assert_eq!(x, Some(5));
99 }
100}