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}