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