nested_ref/
exclusive.rs

1//! Exclusive reference to data contained in one or more nested [`RefCell`]s
2
3#[cfg(doc)]
4use core::cell::RefCell;
5use core::{
6    cell::{Ref, RefMut},
7    ops::{Deref, DerefMut},
8};
9
10use generic_array::{arr, typenum::U0, ArrayLength, GenericArray};
11
12use crate::clone_arr::clone_arr;
13
14/// Exclusive reference to data contained in one or more nested [`RefCell`]s.
15///
16/// This has the same interface as [`RefMut`], and unlike [`NestedRef`](crate::NestedRef) cannot be
17/// nested further.
18///
19/// # Examples
20///
21/// ```
22/// # use std::cell::RefCell;
23/// # use nested_ref::NestedRef;
24/// // Create a `RefCell` two levels deep and a `NestedRefMut` into it
25/// let rc = RefCell::new(RefCell::new(0));
26/// let nr = NestedRef::new(rc.borrow());
27/// let mut nr = NestedRef::map_ref_mut(nr, RefCell::borrow_mut);
28///
29/// // Mutate contained value
30/// assert_eq!(*nr, 0);
31/// *nr = 1;
32/// assert_eq!(*nr, 1);
33/// ```
34pub struct NestedRefMut<'a, T, N>
35where
36    T: ?Sized,
37    N: ArrayLength<Ref<'a, ()>>,
38{
39    /// Reference into the innermost [`RefCell`]
40    pub(crate) inner: RefMut<'a, T>,
41    /// Type-erased references into the other [`RefCell`]s, from outermost to innermost
42    // We store a `&()` in each array entry. It would be better to store the internal `BorrowRef`
43    // directly, but that's not exposed.
44    pub(crate) outer: GenericArray<Ref<'a, ()>, N>,
45}
46
47impl<'a, T> NestedRefMut<'a, T, U0>
48where
49    T: ?Sized,
50{
51    /// Creates a new reference inside a single [`RefCell`]
52    #[must_use]
53    #[inline]
54    pub fn new(inner: RefMut<'a, T>) -> Self {
55        // Start with an empty array of outer `Ref`s
56        let outer = arr![Ref<'a, ()>;];
57        Self { inner, outer }
58    }
59}
60
61impl<'a, T, N> NestedRefMut<'a, T, N>
62where
63    T: ?Sized,
64    N: ArrayLength<Ref<'a, ()>>,
65{
66    /// Creates a reference to a component of the borrowed data, like [`RefMut::map`].
67    ///
68    /// This is an associated function, because a method would interfere with methods of the same
69    /// name on the contents of the [`RefCell`].
70    #[inline]
71    pub fn map<U, F>(orig: Self, f: F) -> NestedRefMut<'a, U, N>
72    where
73        U: ?Sized,
74        F: FnOnce(&mut T) -> &mut U,
75    {
76        // Outer `Ref` is type-erased, so just map the inner `RefMut`
77        NestedRefMut {
78            inner: RefMut::map(orig.inner, f),
79            outer: orig.outer,
80        }
81    }
82
83    /// Creates a reference to an optional component of the borrowed data, like
84    /// [`RefMut::filter_map`]. The original reference is returned inside an `Err` if the closure
85    /// returns `None`.
86    ///
87    /// This is an associated function, because a method would interfere with methods of the same
88    /// name on the contents of the [`RefCell`].
89    #[inline]
90    #[allow(clippy::missing_errors_doc)]
91    pub fn filter_map<U, F>(orig: Self, f: F) -> Result<NestedRefMut<'a, U, N>, Self>
92    where
93        U: ?Sized,
94        F: FnOnce(&mut T) -> Option<&mut U>,
95    {
96        // Outer `Ref`s remain the same
97        let outer = orig.outer;
98        // Delegate to inner `RefMut` and put the result back together
99        match RefMut::filter_map(orig.inner, f) {
100            Ok(inner) => Ok(NestedRefMut { inner, outer }),
101            Err(inner) => Err(NestedRefMut { inner, outer }),
102        }
103    }
104
105    /// Splits a reference into multiple references for different components of the borrowed data,
106    /// like [`RefMut::map_split`].
107    ///
108    /// This is an associated function, because a method would interfere with methods of the same
109    /// name on the contents of the [`RefCell`].
110    #[inline]
111    pub fn map_split<U, V, F>(orig: Self, f: F) -> (NestedRefMut<'a, U, N>, NestedRefMut<'a, V, N>)
112    where
113        U: ?Sized,
114        V: ?Sized,
115        F: FnOnce(&mut T) -> (&mut U, &mut V),
116    {
117        // We need the outer `Ref`s two times
118        let outer_a = clone_arr(&orig.outer);
119        let outer_b = orig.outer;
120        // Delegate to inner `RefMut` and put the results back together
121        let (inner_a, inner_b) = RefMut::map_split(orig.inner, f);
122        (
123            NestedRefMut {
124                inner: inner_a,
125                outer: outer_a,
126            },
127            NestedRefMut {
128                inner: inner_b,
129                outer: outer_b,
130            },
131        )
132    }
133}
134
135impl<'a, T, N> Deref for NestedRefMut<'a, T, N>
136where
137    T: ?Sized,
138    N: ArrayLength<Ref<'a, ()>>,
139{
140    type Target = T;
141
142    #[inline]
143    fn deref(&self) -> &Self::Target {
144        &self.inner
145    }
146}
147
148impl<'a, T, N> DerefMut for NestedRefMut<'a, T, N>
149where
150    T: ?Sized,
151    N: ArrayLength<Ref<'a, ()>>,
152{
153    #[inline]
154    fn deref_mut(&mut self) -> &mut Self::Target {
155        &mut self.inner
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use core::cell::RefCell;
162
163    use crate::{NestedRef, NestedRefMut};
164
165    /// Tests a `NestedRefMut` that's one level deep
166    #[test]
167    fn simple() {
168        // Create `RefCell` and `NestedRefMut` into it
169        let rc = RefCell::new(0);
170        let mut nr = NestedRefMut::new(rc.borrow_mut());
171
172        assert_eq!(*nr, 0);
173        *nr = 1;
174        assert_eq!(*nr, 1);
175    }
176
177    /// Tests a `NestedRefMut` that's three levels deep
178    #[test]
179    fn deep() {
180        // Create `RefCell` and `NestedRefMut` into it
181        let rc = RefCell::new(RefCell::new(RefCell::new(0)));
182        let nr = NestedRef::new(rc.borrow());
183        let nr = NestedRef::map_ref(nr, RefCell::borrow);
184        let mut nr = NestedRef::map_ref_mut(nr, RefCell::borrow_mut);
185
186        assert_eq!(*nr, 0);
187        *nr = 1;
188        assert_eq!(*nr, 1);
189    }
190
191    /// Tests the `NestedRefMut::map` method
192    #[test]
193    fn map() {
194        // Create `RefCell` and `NestedRefMut` into it
195        let rc = RefCell::new((0, 0));
196        let nr = NestedRefMut::new(rc.borrow_mut());
197        let mut nr = NestedRefMut::map(nr, |x| &mut x.0);
198
199        assert_eq!(*nr, 0);
200        *nr = 1;
201        assert_eq!(*nr, 1);
202    }
203
204    /// Tests the `NestedRefMut::filter_map` method
205    #[test]
206    fn filter_map() {
207        // Create `RefCell` and `NestedRefMut` into it
208        let rc = RefCell::new(0);
209        let nr = NestedRefMut::new(rc.borrow_mut());
210        let nr = NestedRefMut::filter_map::<(), _>(nr, |_| None)
211            .map(|_| ())
212            .expect_err("This filter_map should fail");
213        let mut nr = NestedRefMut::filter_map(nr, |x| Some(x))
214            .map_err(|_| ())
215            .expect("This filter_map should succeed");
216
217        assert_eq!(*nr, 0);
218        *nr = 1;
219        assert_eq!(*nr, 1);
220    }
221
222    /// Tests the `NestedRefMut::map_split` method
223    #[test]
224    fn map_split() {
225        // Create `RefCell` and `NestedRefMut` into it
226        let rc = RefCell::new((0, 0));
227        let nr = NestedRefMut::new(rc.borrow_mut());
228        let (mut nr, _nr2) = NestedRefMut::map_split(nr, |x| (&mut x.0, &mut x.1));
229
230        assert_eq!(*nr, 0);
231        *nr = 1;
232        assert_eq!(*nr, 1);
233    }
234}