1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
//! This crate provides special handling for reference primitive type. //! //! We know that the value a reference pointers to is kept alive during the full //! lifetime of the reference. //! //! Many times a mutable reference is consumed by some specific handling, //! but they didn't bother return the mutable reference back to the caller even //! if they didn't give meaningful results. At most time this is fine, since you can //! just recreate the reference by borrow again. //! However there is a problem when the reference comes from an argument. //! //! This crate catches this pattern and provide it as a trait `TryTransform`, //! making it possible without any NLL support. //! //! ``` //! # use std::hash::Hash; //! # use std::collections::HashMap; //! # //! use try_transform_mut::TryTransform; //! //! fn get_default<'r, K: Hash + Eq + Copy, V: Default>( //! map: &'r mut HashMap<K, V>, //! key: K, //! ) -> &'r mut V { //! match map.try_transform(|m| m.get_mut(&key)) { //! Ok(value) => { //! value //! } //! Err(map) => { //! map.insert(key, V::default()); //! map.get_mut(&key).unwrap() //! } //! } //! } //! ``` //! /// A trait providing `try_transform` method to reference primitive type. pub trait TryTransform { /// Try to consume a reference and transform it to `Some(B)` with a closure. /// If failed and returned `None`, return the original reference value. /// /// Especially useful for mutable references. fn try_transform<B, F>(self, f: F) -> Result<B, Self> where Self: Sized, F: FnOnce(Self) -> Option<B>; } impl<'a, T> TryTransform for &'a T { fn try_transform<B, F>(self, f: F) -> Result<B, Self> where Self: Sized, F: FnOnce(Self) -> Option<B>, { if let Some(v) = f(self) { return Ok(v); } Err(self) } } impl<'a, T> TryTransform for &'a mut T { fn try_transform<B, F>(self, f: F) -> Result<B, Self> where Self: Sized, F: FnOnce(Self) -> Option<B>, { let this: *mut T = self as _; if let Some(v) = f(self) { return Ok(v); } Err(unsafe { this.as_mut().unwrap() }) } } #[cfg(test)] mod tests { use std::hash::Hash; use std::collections::HashMap; use super::TryTransform; fn get_default<'r, K: Hash + Eq + Copy, V: Default>( map: &'r mut HashMap<K, V>, key: K, ) -> &'r mut V { match map.try_transform(|m| m.get_mut(&key)) { Ok(value) => value, Err(map) => { map.insert(key, V::default()); map.get_mut(&key).unwrap() } } } #[test] fn it_works() { let mut a: HashMap<usize, usize> = HashMap::new(); get_default(&mut a, 2); } }