// Copyright 2021, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! The TryInsert trait adds to Option the method //! get_or_try_to_insert_with, which is analogous to //! get_or_insert_with, but allows the called function to fail and propagates the failure. /// The TryInsert trait adds to Option the method /// get_or_try_to_insert_with, which is analogous to /// get_or_insert_with, but allows the called function to fail and propagates the failure. pub trait TryInsert { /// Type of the Ok branch of the Result type Item; /// Inserts a value computed from `f` into the option if it is [`None`], /// then returns a mutable reference to the contained value. If `f` /// returns Err, the Option is unchanged. /// /// # Examples /// /// ``` /// let mut x = None; /// assert_eq!(x.get_or_try_to_insert_with(Err("oops".to_string())), Err("oops".to_string())) /// { /// let y: &mut u32 = x.get_or_try_to_insert_with(|| Ok(5))?; /// assert_eq!(y, &5); /// /// *y = 7; /// } /// /// assert_eq!(x, Some(7)); /// ``` fn get_or_try_to_insert_with Result>( &mut self, f: F, ) -> Result<&mut Self::Item, E>; } impl TryInsert for Option { type Item = T; fn get_or_try_to_insert_with Result>( &mut self, f: F, ) -> Result<&mut Self::Item, E> { if self.is_none() { *self = Some(f()?); } match self { Some(v) => Ok(v), // SAFETY: a `None` variant for `self` would have been replaced by a `Some` // variant in the code above. None => unsafe { std::hint::unreachable_unchecked() }, } } } #[cfg(test)] mod test { use super::*; fn fails() -> Result { Err("fail".to_string()) } fn succeeds() -> Result { Ok(99) } #[test] fn test() { let mut x = None; assert_eq!(x.get_or_try_to_insert_with(fails), Err("fail".to_string())); assert_eq!(x, None); assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 99); assert_eq!(x, Some(99)); x = Some(42); assert_eq!(*x.get_or_try_to_insert_with(fails).unwrap(), 42); assert_eq!(x, Some(42)); assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 42); assert_eq!(x, Some(42)); *x.get_or_try_to_insert_with(fails).unwrap() = 2; assert_eq!(x, Some(2)); *x.get_or_try_to_insert_with(succeeds).unwrap() = 3; assert_eq!(x, Some(3)); x = None; *x.get_or_try_to_insert_with(succeeds).unwrap() = 5; assert_eq!(x, Some(5)); } }