blob: 6dd396201ded7d816a649c7de9935e8dc204fad7 [file] [log] [blame]
// 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<T> 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<T> 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<E, F: FnOnce() -> Result<Self::Item, E>>(
&mut self,
f: F,
) -> Result<&mut Self::Item, E>;
}
impl<T> TryInsert for Option<T> {
type Item = T;
fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
&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<i32, String> {
Err("fail".to_string())
}
fn succeeds() -> Result<i32, String> {
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));
}
}