1 //! Helper for temporarily taking values out and then putting them back in. 2 3 /// An RAII type to temporarily take a `U` out of a `T` and then put it back 4 /// again on drop. 5 /// 6 /// This allows you to split borrows, if necessary, to satisfy the borrow 7 /// checker. 8 /// 9 /// The `F` type parameter must project from the container type `T` to its `U` 10 /// that we want to temporarily take out of it. 11 /// 12 /// # Example 13 /// 14 /// ``` 15 /// use cranelift_codegen::TakeAndReplace; 16 /// 17 /// #[derive(Default)] 18 /// struct BigContextStruct { 19 /// items: Vec<u32>, 20 /// count: usize, 21 /// } 22 /// 23 /// impl BigContextStruct { 24 /// fn handle_item(&mut self, item: u32) { 25 /// self.count += 1; 26 /// println!("Handled {item}!"); 27 /// } 28 /// } 29 /// 30 /// let mut ctx = BigContextStruct::default(); 31 /// ctx.items.extend([42, 1337, 1312]); 32 /// 33 /// { 34 /// // Temporarily take `self.items` out of `ctx`. 35 /// let mut guard = TakeAndReplace::new(&mut ctx, |ctx| &mut ctx.items); 36 /// let (ctx, items) = guard.get(); 37 /// 38 /// // Now we can both borrow/iterate/mutate `items` and call `&mut self` helper 39 /// // methods on `ctx`. This would not otherwise be possible if we didn't split 40 /// // the borrows, since Rust's borrow checker doesn't see through methods and 41 /// // know that `handle_item` doesn't use `self.items`. 42 /// for item in items.drain(..) { 43 /// ctx.handle_item(item); 44 /// } 45 /// } 46 /// 47 /// // When `guard` is dropped, `items` is replaced in `ctx`, allowing us to 48 /// // reuse its capacity and avoid future allocations. ``` 49 /// assert!(ctx.items.capacity() >= 3); 50 /// ``` 51 pub struct TakeAndReplace<'a, T, U, F> 52 where 53 F: Fn(&mut T) -> &mut U, 54 U: Default, 55 { 56 container: &'a mut T, 57 value: U, 58 proj: F, 59 } 60 61 impl<'a, T, U, F> Drop for TakeAndReplace<'a, T, U, F> 62 where 63 F: Fn(&mut T) -> &mut U, 64 U: Default, 65 { drop(&mut self)66 fn drop(&mut self) { 67 *(self.proj)(self.container) = core::mem::take(&mut self.value); 68 } 69 } 70 71 impl<'a, T, U, F> TakeAndReplace<'a, T, U, F> 72 where 73 F: Fn(&mut T) -> &mut U, 74 U: Default, 75 { 76 /// Create a new `TakeAndReplace` that temporarily takes out 77 /// `proj(container)`. new(mut container: &'a mut T, proj: F) -> Self78 pub fn new(mut container: &'a mut T, proj: F) -> Self { 79 let value = core::mem::take(proj(&mut container)); 80 TakeAndReplace { 81 container, 82 value, 83 proj, 84 } 85 } 86 87 /// Get the underlying container and taken-out value. get(&mut self) -> (&mut T, &mut U)88 pub fn get(&mut self) -> (&mut T, &mut U) { 89 (&mut *self.container, &mut self.value) 90 } 91 } 92