cranelift_codegen/
take_and_replace.rs

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/// ```
51pub struct TakeAndReplace<'a, T, U, F>
52where
53    F: Fn(&mut T) -> &mut U,
54    U: Default,
55{
56    container: &'a mut T,
57    value: U,
58    proj: F,
59}
60
61impl<'a, T, U, F> Drop for TakeAndReplace<'a, T, U, F>
62where
63    F: Fn(&mut T) -> &mut U,
64    U: Default,
65{
66    fn drop(&mut self) {
67        *(self.proj)(self.container) = std::mem::take(&mut self.value);
68    }
69}
70
71impl<'a, T, U, F> TakeAndReplace<'a, T, U, F>
72where
73    F: Fn(&mut T) -> &mut U,
74    U: Default,
75{
76    /// Create a new `TakeAndReplace` that temporarily takes out
77    /// `proj(container)`.
78    pub fn new(mut container: &'a mut T, proj: F) -> Self {
79        let value = std::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.
88    pub fn get(&mut self) -> (&mut T, &mut U) {
89        (&mut *self.container, &mut self.value)
90    }
91}