nested_ref/shared.rs
1//! Shared 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::{Add, Deref},
8 ptr::addr_of,
9};
10
11use generic_array::{
12 arr,
13 sequence::Lengthen,
14 typenum::{Add1, B1, U0},
15 ArrayLength, GenericArray,
16};
17
18use crate::{clone_arr::clone_arr, NestedRefMut};
19
20/// Shared reference to data contained in one or more nested [`RefCell`]s.
21///
22/// This has the same interface as [`Ref`], with the additional methods
23/// [`map_ref`](NestedRef::map_ref) and [`map_ref_mut`](NestedRef::map_ref_mut) that form references
24/// into nested [`RefCell`]s.
25///
26/// # Examples
27///
28/// ```
29/// # use std::cell::{Cell, RefCell};
30/// # use nested_ref::NestedRef;
31/// // Create a `RefCell` two levels deep and a `NestedRef` into it
32/// let rc = RefCell::new(RefCell::new(Cell::new(0)));
33/// let nr = NestedRef::new(rc.borrow());
34/// let nr = NestedRef::map_ref(nr, RefCell::borrow);
35/// assert_eq!(nr.get(), 0);
36///
37/// // Mutate through `NestedRef`
38/// nr.set(1);
39/// assert_eq!(nr.get(), 1);
40///
41/// // Mutate through independent `Ref`
42/// rc.borrow().borrow().set(2);
43/// assert_eq!(nr.get(), 2);
44/// ```
45pub struct NestedRef<'a, T, N>
46where
47 T: ?Sized,
48 N: ArrayLength<Ref<'a, ()>>,
49{
50 /// Reference into the innermost [`RefCell`]
51 inner: Ref<'a, T>,
52 /// Type-erased references into the other [`RefCell`]s, from outermost to innermost
53 // We store a `&()` in each array entry. It would be better to store the internal `BorrowRef`
54 // directly, but that's not exposed.
55 outer: GenericArray<Ref<'a, ()>, N>,
56}
57
58impl<'a, T> NestedRef<'a, T, U0>
59where
60 T: ?Sized,
61{
62 /// Creates a new reference inside a single [`RefCell`]
63 #[must_use]
64 #[inline]
65 pub fn new(inner: Ref<'a, T>) -> Self {
66 // Start with an empty array of outer `Ref`s
67 let outer = arr![Ref<'a, ()>;];
68 Self { inner, outer }
69 }
70}
71
72impl<'a, T, N> NestedRef<'a, T, N>
73where
74 T: ?Sized,
75 N: ArrayLength<Ref<'a, ()>>,
76{
77 /// Clones the reference, like [`Ref::clone`].
78 ///
79 /// This is an associated function, because a method would interfere with methods of the same
80 /// name on the contents of the [`RefCell`].
81 #[must_use]
82 #[inline]
83 #[allow(clippy::should_implement_trait)]
84 pub fn clone(orig: &Self) -> Self {
85 // Just clone all contained `Ref`s
86 Self {
87 inner: Ref::clone(&orig.inner),
88 outer: clone_arr(&orig.outer),
89 }
90 }
91
92 /// Creates a reference to a component of the borrowed data, like [`Ref::map`].
93 ///
94 /// This is an associated function, because a method would interfere with methods of the same
95 /// name on the contents of the [`RefCell`].
96 #[inline]
97 pub fn map<U, F>(orig: Self, f: F) -> NestedRef<'a, U, N>
98 where
99 U: ?Sized,
100 F: FnOnce(&T) -> &U,
101 {
102 // Outer `Ref` is type-erased, so just map the inner `Ref`
103 NestedRef {
104 inner: Ref::map(orig.inner, f),
105 outer: orig.outer,
106 }
107 }
108
109 /// Creates a reference to an optional component of the borrowed data, like [`Ref::filter_map`].
110 /// The original reference is returned inside an `Err` if the closure returns `None`.
111 ///
112 /// This is an associated function, because a method would interfere with methods of the same
113 /// name on the contents of the [`RefCell`].
114 #[inline]
115 #[allow(clippy::missing_errors_doc)]
116 pub fn filter_map<U, F>(orig: Self, f: F) -> Result<NestedRef<'a, U, N>, Self>
117 where
118 U: ?Sized,
119 F: FnOnce(&T) -> Option<&U>,
120 {
121 // Outer `Ref`s remain the same
122 let outer = orig.outer;
123 // Delegate to inner `Ref` and put the result back together
124 match Ref::filter_map(orig.inner, f) {
125 Ok(inner) => Ok(NestedRef { inner, outer }),
126 Err(inner) => Err(NestedRef { inner, outer }),
127 }
128 }
129
130 /// Splits a reference into multiple references for different components of the borrowed data,
131 /// like [`Ref::map_split`].
132 ///
133 /// This is an associated function, because a method would interfere with methods of the same
134 /// name on the contents of the [`RefCell`].
135 #[inline]
136 pub fn map_split<U, V, F>(orig: Self, f: F) -> (NestedRef<'a, U, N>, NestedRef<'a, V, N>)
137 where
138 U: ?Sized,
139 V: ?Sized,
140 F: FnOnce(&T) -> (&U, &V),
141 {
142 // We need the outer `Ref`s two times
143 let outer_a = clone_arr(&orig.outer);
144 let outer_b = orig.outer;
145 // Delegate to inner `Ref` and put the results back together
146 let (inner_a, inner_b) = Ref::map_split(orig.inner, f);
147 (
148 NestedRef {
149 inner: inner_a,
150 outer: outer_a,
151 },
152 NestedRef {
153 inner: inner_b,
154 outer: outer_b,
155 },
156 )
157 }
158}
159
160impl<'a, T, N> NestedRef<'a, T, N>
161where
162 T: ?Sized,
163 N: ArrayLength<Ref<'a, ()>> + Add<B1>,
164 <N as Add<B1>>::Output: ArrayLength<Ref<'a, ()>>,
165 GenericArray<Ref<'a, ()>, N>:
166 Lengthen<Ref<'a, ()>, Longer = GenericArray<Ref<'a, ()>, Add1<N>>>,
167{
168 /// Creates a shared reference to a component of the borrowed data that is contained in a nested
169 /// [`RefCell`].
170 ///
171 /// This is an associated function, because a method would interfere with methods of the same
172 /// name on the contents of the [`RefCell`].
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// # use std::cell::RefCell;
178 /// # use nested_ref::NestedRef;
179 /// let c = RefCell::new(('a', RefCell::new(2)));
180 /// let b1 = NestedRef::new(c.borrow());
181 /// let b2 = NestedRef::map_ref(b1, |t| t.1.borrow());
182 /// assert_eq!(*b2, 2);
183 /// ```
184 #[inline]
185 pub fn map_ref<U, F>(orig: Self, f: F) -> NestedRef<'a, U, Add1<N>>
186 where
187 U: ?Sized,
188 F: FnOnce(&T) -> Ref<'_, U>,
189 {
190 // Safety: The array of outer references is kept alive as long as the returned object
191 let (t, outer) = unsafe { orig.nest() };
192 // Apply mapping function to obtain new inner `Ref`
193 let inner = f(t);
194 // Bundle all `Ref`s together
195 NestedRef { inner, outer }
196 }
197
198 /// Creates an exclusive reference to a component of the borrowed data that is contained in a
199 /// nested [`RefCell`].
200 ///
201 /// This is an associated function, because a method would interfere with methods of the same
202 /// name on the contents of the [`RefCell`].
203 ///
204 /// # Examples
205 ///
206 /// ```
207 /// # use std::cell::RefCell;
208 /// # use nested_ref::NestedRef;
209 /// let c = RefCell::new(('a', RefCell::new(2)));
210 /// let b1 = NestedRef::new(c.borrow());
211 /// let mut b2 = NestedRef::map_ref_mut(b1, |t| t.1.borrow_mut());
212 /// assert_eq!(*b2, 2);
213 /// *b2 = 3;
214 /// ```
215 #[inline]
216 pub fn map_ref_mut<U, F>(orig: Self, f: F) -> NestedRefMut<'a, U, Add1<N>>
217 where
218 U: ?Sized,
219 F: FnOnce(&T) -> RefMut<'_, U>,
220 {
221 // Safety: The array of outer references is kept alive as long as the returned object
222 let (t, outer) = unsafe { orig.nest() };
223 // Apply mapping function to obtain new inner `RefMut`
224 let inner = f(t);
225 // Bundle all `Ref`s together
226 NestedRefMut { inner, outer }
227 }
228
229 /// Increases the nesting level by appending the innermost reference to the outer references.
230 /// Returns the innermost reference as a plain reference, along with the new array of outer
231 /// references.
232 ///
233 /// # Safety
234 ///
235 /// The returned array must be kept alive as long as the returned reference.
236 #[must_use]
237 #[inline]
238 unsafe fn nest(self) -> (&'a T, GenericArray<Ref<'a, ()>, Add1<N>>) {
239 // Get a reference to the `T` that is independent of the inner `Ref`
240 let t = addr_of!(*self.inner);
241 // Safety: The inner `Ref` is kept alive as long as this reference, as guaranteed by the
242 // caller
243 let t: &'a T = unsafe { &*t };
244 // Type-erase old inner `Ref` and append it to the outer `Ref`s
245 let new = Ref::map(self.inner, |_| &());
246 let outer = self.outer.append(new);
247 // Return plain reference and array of outer references
248 (t, outer)
249 }
250}
251
252impl<'a, T, N> Deref for NestedRef<'a, T, N>
253where
254 T: ?Sized,
255 N: ArrayLength<Ref<'a, ()>>,
256{
257 type Target = T;
258
259 #[inline]
260 fn deref(&self) -> &Self::Target {
261 &self.inner
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use core::cell::{Cell, RefCell};
268
269 use crate::NestedRef;
270
271 /// Tests a `NestedRef` that's one level deep
272 #[test]
273 fn simple() {
274 // Create `RefCell` and `NestedRef` into it
275 let rc = RefCell::new(Cell::new(0));
276 let nr = NestedRef::new(rc.borrow());
277 assert_eq!(nr.get(), 0);
278 assert_eq!(rc.borrow().get(), 0);
279 // Mutate through `NestedRef`
280 nr.set(1);
281 assert_eq!(nr.get(), 1);
282 assert_eq!(rc.borrow().get(), 1);
283 // Mutate through independent `Ref`
284 rc.borrow().set(2);
285 assert_eq!(nr.get(), 2);
286 assert_eq!(rc.borrow().get(), 2);
287 }
288
289 /// Tests a `NestedRef` that's three levels deep
290 #[test]
291 fn deep() {
292 // Create `RefCell` and `NestedRef` into it
293 let rc = RefCell::new(RefCell::new(RefCell::new(Cell::new(0))));
294 let nr = NestedRef::new(rc.borrow());
295 let nr = NestedRef::map_ref(nr, RefCell::borrow);
296 let nr = NestedRef::map_ref(nr, RefCell::borrow);
297 assert_eq!(nr.get(), 0);
298 assert_eq!(rc.borrow().borrow().borrow().get(), 0);
299 // Mutate through `NestedRef`
300 nr.set(1);
301 assert_eq!(nr.get(), 1);
302 assert_eq!(rc.borrow().borrow().borrow().get(), 1);
303 // Mutate through independent `Ref`
304 rc.borrow().borrow().borrow().set(2);
305 assert_eq!(nr.get(), 2);
306 assert_eq!(rc.borrow().borrow().borrow().get(), 2);
307 }
308
309 /// Tests the `NestedRef::clone` method
310 #[test]
311 fn clone() {
312 // Create `RefCell` and `NestedRef` into it
313 let rc = RefCell::new(Cell::new(0));
314 let nr = NestedRef::new(rc.borrow());
315 assert_eq!(nr.get(), 0);
316 assert_eq!(NestedRef::clone(&nr).get(), 0);
317 // Mutate through `NestedRef`
318 nr.set(1);
319 assert_eq!(nr.get(), 1);
320 assert_eq!(NestedRef::clone(&nr).get(), 1);
321 // Mutate through clone
322 NestedRef::clone(&nr).set(2);
323 assert_eq!(nr.get(), 2);
324 assert_eq!(NestedRef::clone(&nr).get(), 2);
325 }
326
327 /// Tests the `NestedRef::map` method
328 #[test]
329 fn map() {
330 // Create `RefCell` and `NestedRef` into it
331 let rc = RefCell::new((Cell::new(0), Cell::new(0)));
332 let nr = NestedRef::new(rc.borrow());
333 let nr = NestedRef::map(nr, |x| &x.0);
334 assert_eq!(nr.get(), 0);
335 assert_eq!(rc.borrow().0.get(), 0);
336 // Mutate through `NestedRef`
337 nr.set(1);
338 assert_eq!(nr.get(), 1);
339 assert_eq!(rc.borrow().0.get(), 1);
340 // Mutate through independent `Ref`
341 rc.borrow().0.set(2);
342 assert_eq!(nr.get(), 2);
343 assert_eq!(rc.borrow().0.get(), 2);
344 }
345
346 /// Tests the `NestedRef::filter_map` method
347 #[test]
348 fn filter_map() {
349 // Create `RefCell` and `NestedRef` into it
350 let rc = RefCell::new(Cell::new(0));
351 let nr = NestedRef::new(rc.borrow());
352 let nr = NestedRef::filter_map::<(), _>(nr, |_| None)
353 .map(|_| ())
354 .expect_err("This filter_map should fail");
355 let nr = NestedRef::filter_map(nr, |x| Some(x))
356 .map_err(|_| ())
357 .expect("This filter_map should succeed");
358 assert_eq!(nr.get(), 0);
359 assert_eq!(rc.borrow().get(), 0);
360 // Mutate through `NestedRef`
361 nr.set(1);
362 assert_eq!(nr.get(), 1);
363 assert_eq!(rc.borrow().get(), 1);
364 // Mutate through independent `Ref`
365 rc.borrow().set(2);
366 assert_eq!(nr.get(), 2);
367 assert_eq!(rc.borrow().get(), 2);
368 }
369
370 /// Tests the `NestedRef::map_split` method
371 #[test]
372 fn map_split() {
373 // Create `RefCell` and `NestedRef` into it
374 let rc = RefCell::new((Cell::new(0), Cell::new(0)));
375 let nr = NestedRef::new(rc.borrow());
376 let (nr, _nr2) = NestedRef::map_split(nr, |x| (&x.0, &x.1));
377 assert_eq!(nr.get(), 0);
378 assert_eq!(rc.borrow().0.get(), 0);
379 // Mutate through `NestedRef`
380 nr.set(1);
381 assert_eq!(nr.get(), 1);
382 assert_eq!(rc.borrow().0.get(), 1);
383 // Mutate through independent `Ref`
384 rc.borrow().0.set(2);
385 assert_eq!(nr.get(), 2);
386 assert_eq!(rc.borrow().0.get(), 2);
387 }
388}