Skip to main content

lender/adapters/
flatten.rs

1use aliasable::boxed::AliasableBox;
2use core::fmt;
3use maybe_dangling::MaybeDangling;
4
5use crate::{Covar, FusedLender, IntoLender, Lend, Lender, Lending, Map, try_trait_v2::Try};
6
7/// A lender that flattens one level of nesting in a lender of lenders.
8///
9/// This `struct` is created by the
10/// [`flatten()`](crate::Lender::flatten) method on [`Lender`].
11#[must_use = "lenders are lazy and do nothing unless consumed"]
12pub struct Flatten<'this, L: Lender>
13where
14    for<'all> Lend<'all, L>: IntoLender,
15{
16    inner: FlattenCompat<'this, L>,
17}
18
19impl<L: Lender> Flatten<'_, L>
20where
21    for<'all> Lend<'all, L>: IntoLender,
22{
23    #[inline]
24    pub(crate) fn new(lender: L) -> Self {
25        let _ = L::__check_covariance(crate::CovariantProof::new());
26        Self {
27            inner: FlattenCompat::new(lender),
28        }
29    }
30
31    /// Returns the inner lender.
32    #[inline(always)]
33    pub fn into_inner(self) -> L {
34        *AliasableBox::into_unique(self.inner.lender)
35    }
36}
37
38// Clone is not implemented for Flatten because the inner sub-lender may
39// reference the AliasableBox allocation; a clone would create a new allocation
40// but the cloned inner sub-lender would still reference the original.
41
42impl<L: Lender + fmt::Debug> fmt::Debug for Flatten<'_, L>
43where
44    for<'all> Lend<'all, L>: IntoLender,
45    for<'all> <Lend<'all, L> as IntoLender>::Lender: fmt::Debug,
46{
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.debug_struct("Flatten")
49            .field("inner", &self.inner)
50            .finish()
51    }
52}
53
54impl<'lend, 'this, L: Lender> Lending<'lend> for Flatten<'this, L>
55where
56    for<'all> Lend<'all, L>: IntoLender,
57{
58    type Lend = Lend<'lend, <Lend<'this, L> as IntoLender>::Lender>;
59}
60
61impl<L: Lender> Lender for Flatten<'_, L>
62where
63    for<'all> Lend<'all, L>: IntoLender,
64{
65    // SAFETY: the lend is that of the inner lender
66    crate::unsafe_assume_covariance!();
67    #[inline(always)]
68    fn next(&mut self) -> Option<Lend<'_, Self>> {
69        self.inner.next()
70    }
71
72    #[inline(always)]
73    fn size_hint(&self) -> (usize, Option<usize>) {
74        self.inner.size_hint()
75    }
76
77    #[inline(always)]
78    fn try_fold<B, F, R>(&mut self, init: B, f: F) -> R
79    where
80        Self: Sized,
81        F: FnMut(B, Lend<'_, Self>) -> R,
82        R: Try<Output = B>,
83    {
84        self.inner.try_fold(init, f)
85    }
86
87    #[inline(always)]
88    fn fold<B, F>(self, init: B, f: F) -> B
89    where
90        Self: Sized,
91        F: FnMut(B, Lend<'_, Self>) -> B,
92    {
93        self.inner.fold(init, f)
94    }
95
96    #[inline(always)]
97    fn count(self) -> usize
98    where
99        Self: Sized,
100    {
101        self.inner.count()
102    }
103}
104
105impl<L: FusedLender> FusedLender for Flatten<'_, L> where for<'all> Lend<'all, L>: IntoLender {}
106
107/// A lender that maps each element to a lender, and yields the elements of
108/// the produced lenders.
109///
110/// This `struct` is created by the
111/// [`flat_map()`](crate::Lender::flat_map) method on [`Lender`].
112#[must_use = "lenders are lazy and do nothing unless consumed"]
113pub struct FlatMap<'this, L: Lender, F>
114where
115    Map<L, F>: Lender,
116    for<'all> Lend<'all, Map<L, F>>: IntoLender,
117{
118    inner: FlattenCompat<'this, Map<L, F>>,
119}
120
121impl<L: Lender, F> FlatMap<'_, L, F>
122where
123    Map<L, F>: Lender,
124    for<'all> Lend<'all, Map<L, F>>: IntoLender,
125{
126    #[inline]
127    pub(crate) fn new(lender: L, f: Covar<F>) -> Self {
128        let _ = L::__check_covariance(crate::CovariantProof::new());
129        Self {
130            inner: FlattenCompat::new(Map::new(lender, f)),
131        }
132    }
133
134    /// Returns the inner lender.
135    #[inline(always)]
136    pub fn into_inner(self) -> L {
137        (*AliasableBox::into_unique(self.inner.lender)).into_inner()
138    }
139
140    /// Returns the inner lender and the mapping function.
141    #[inline(always)]
142    pub fn into_parts(self) -> (L, Covar<F>) {
143        (*AliasableBox::into_unique(self.inner.lender)).into_parts()
144    }
145}
146
147// Clone is not implemented for FlatMap because the inner sub-lender may
148// reference the AliasableBox allocation; a clone would create a new allocation
149// but the cloned inner sub-lender would still reference the original.
150
151impl<L: Lender + fmt::Debug, F> fmt::Debug for FlatMap<'_, L, F>
152where
153    Map<L, F>: Lender,
154    for<'all> Lend<'all, Map<L, F>>: IntoLender,
155    for<'all> <Lend<'all, Map<L, F>> as IntoLender>::Lender: fmt::Debug,
156{
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        f.debug_struct("FlatMap")
159            .field("inner", &self.inner)
160            .finish()
161    }
162}
163
164impl<'lend, 'this, L: Lender, F> Lending<'lend> for FlatMap<'this, L, F>
165where
166    Map<L, F>: Lender,
167    for<'all> Lend<'all, Map<L, F>>: IntoLender,
168{
169    type Lend = Lend<'lend, <Lend<'this, Map<L, F>> as IntoLender>::Lender>;
170}
171
172impl<L: Lender, F> Lender for FlatMap<'_, L, F>
173where
174    Map<L, F>: Lender,
175    for<'all> Lend<'all, Map<L, F>>: IntoLender,
176{
177    // SAFETY: the lend is that of the inner lender
178    crate::unsafe_assume_covariance!();
179    #[inline(always)]
180    fn next(&mut self) -> Option<Lend<'_, Self>> {
181        self.inner.next()
182    }
183
184    #[inline(always)]
185    fn size_hint(&self) -> (usize, Option<usize>) {
186        self.inner.size_hint()
187    }
188
189    #[inline(always)]
190    fn try_fold<B, G, R>(&mut self, init: B, f: G) -> R
191    where
192        Self: Sized,
193        G: FnMut(B, Lend<'_, Self>) -> R,
194        R: Try<Output = B>,
195    {
196        self.inner.try_fold(init, f)
197    }
198
199    #[inline(always)]
200    fn fold<B, G>(self, init: B, f: G) -> B
201    where
202        Self: Sized,
203        G: FnMut(B, Lend<'_, Self>) -> B,
204    {
205        self.inner.fold(init, f)
206    }
207
208    #[inline(always)]
209    fn count(self) -> usize
210    where
211        Self: Sized,
212    {
213        self.inner.count()
214    }
215}
216
217impl<L: FusedLender, F> FusedLender for FlatMap<'_, L, F>
218where
219    Map<L, F>: Lender,
220    for<'all> Lend<'all, Map<L, F>>: IntoLender,
221{
222}
223
224pub(crate) struct FlattenCompat<'this, L: Lender>
225where
226    for<'all> Lend<'all, L>: IntoLender,
227{
228    // MaybeDangling wraps the inner lender to indicate it may reference data
229    // from the outer lender. AliasableBox eliminates noalias retagging that would
230    // invalidate the inner reference when the struct is moved.
231    // Field order ensures outer lender drops last.
232    //
233    // See https://github.com/WanderLanz/Lender/issues/34
234    inner: MaybeDangling<Option<<Lend<'this, L> as IntoLender>::Lender>>,
235    lender: AliasableBox<L>,
236}
237
238impl<L: Lender> FlattenCompat<'_, L>
239where
240    for<'all> Lend<'all, L>: IntoLender,
241{
242    #[inline]
243    pub(crate) fn new(lender: L) -> Self {
244        let _ = L::__check_covariance(crate::CovariantProof::new());
245        Self {
246            inner: MaybeDangling::new(None),
247            lender: AliasableBox::from_unique(alloc::boxed::Box::new(lender)),
248        }
249    }
250}
251
252// Clone is not implemented for FlattenCompat because the inner sub-lender may
253// reference the AliasableBox allocation; a clone would create a new allocation
254// but the cloned inner sub-lender would still reference the original.
255
256impl<L: Lender + fmt::Debug> fmt::Debug for FlattenCompat<'_, L>
257where
258    for<'all> Lend<'all, L>: IntoLender,
259    for<'all> <Lend<'all, L> as IntoLender>::Lender: fmt::Debug,
260{
261    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262        f.debug_struct("FlattenCompat")
263            .field("lender", &self.lender)
264            .field("inner", &self.inner)
265            .finish()
266    }
267}
268
269impl<'lend, 'this, L: Lender> Lending<'lend> for FlattenCompat<'this, L>
270where
271    for<'all> Lend<'all, L>: IntoLender,
272{
273    type Lend = Lend<'lend, <Lend<'this, L> as IntoLender>::Lender>;
274}
275
276impl<'this, L: Lender> Lender for FlattenCompat<'this, L>
277where
278    for<'all> Lend<'all, L>: IntoLender,
279{
280    // SAFETY: the lend is that of the inner lender
281    crate::unsafe_assume_covariance!();
282    #[inline]
283    #[allow(clippy::question_mark)]
284    fn next(&mut self) -> Option<Lend<'_, Self>> {
285        loop {
286            // SAFETY: Polonius return
287            #[allow(clippy::deref_addrof)]
288            let reborrow = unsafe { &mut *(&raw mut *self.inner) };
289            if let Some(inner) = reborrow {
290                if let Some(x) = inner.next() {
291                    return Some(x);
292                }
293            }
294
295            // SAFETY: inner is manually guaranteed to be
296            // the only lend alive of the inner lender
297            *self.inner = self.lender.next().map(|l| unsafe {
298                core::mem::transmute::<
299                    <Lend<'_, L> as IntoLender>::Lender,
300                    <Lend<'this, L> as IntoLender>::Lender,
301                >(l.into_lender())
302            });
303
304            if self.inner.is_none() {
305                return None;
306            }
307        }
308    }
309
310    #[inline]
311    fn size_hint(&self) -> (usize, Option<usize>) {
312        let inner_len = match &*self.inner {
313            Some(inner) => inner.size_hint().0,
314            None => 0,
315        };
316        (inner_len, None)
317    }
318
319    #[inline]
320    fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
321    where
322        Self: Sized,
323        F: FnMut(B, Lend<'_, Self>) -> R,
324        R: Try<Output = B>,
325    {
326        use core::ops::ControlFlow;
327        let mut acc = init;
328        if let Some(ref mut inner) = *self.inner {
329            match inner.try_fold(acc, &mut f).branch() {
330                ControlFlow::Continue(b) => acc = b,
331                ControlFlow::Break(r) => return R::from_residual(r),
332            }
333        }
334        *self.inner = None;
335        loop {
336            let Some(l) = self.lender.next() else { break };
337            // SAFETY: inner is manually guaranteed to be
338            // the only lend alive of the inner lender
339            *self.inner = Some(unsafe {
340                core::mem::transmute::<
341                    <Lend<'_, L> as IntoLender>::Lender,
342                    <Lend<'this, L> as IntoLender>::Lender,
343                >(l.into_lender())
344            });
345            if let Some(ref mut inner) = *self.inner {
346                match inner.try_fold(acc, &mut f).branch() {
347                    ControlFlow::Continue(b) => acc = b,
348                    ControlFlow::Break(r) => return R::from_residual(r),
349                }
350            }
351            *self.inner = None;
352        }
353        R::from_output(acc)
354    }
355
356    #[inline]
357    fn fold<B, F>(mut self, init: B, mut f: F) -> B
358    where
359        Self: Sized,
360        F: FnMut(B, Lend<'_, Self>) -> B,
361    {
362        let mut acc = init;
363        if let Some(inner) = self.inner.take() {
364            acc = inner.fold(acc, &mut f);
365        }
366        while let Some(l) = self.lender.next() {
367            // SAFETY: inner is manually guaranteed to be
368            // the only lend alive of the inner lender
369            let sub = unsafe {
370                core::mem::transmute::<
371                    <Lend<'_, L> as IntoLender>::Lender,
372                    <Lend<'this, L> as IntoLender>::Lender,
373                >(l.into_lender())
374            };
375            acc = sub.fold(acc, &mut f);
376        }
377        acc
378    }
379
380    #[inline]
381    fn count(self) -> usize
382    where
383        Self: Sized,
384    {
385        self.fold(0, |count, _| count + 1)
386    }
387}
388
389impl<L: FusedLender> FusedLender for FlattenCompat<'_, L> where for<'all> Lend<'all, L>: IntoLender {}
390
391#[cfg(test)]
392mod test {
393    use super::*;
394
395    struct Parent([i32; 4]);
396
397    impl<'lend> Lending<'lend> for Parent {
398        type Lend = Child<'lend>;
399    }
400
401    impl Lender for Parent {
402        crate::check_covariance!();
403        fn next(&mut self) -> Option<Lend<'_, Self>> {
404            Some(Child { array_ref: &self.0 })
405        }
406    }
407
408    struct Child<'a> {
409        array_ref: &'a [i32; 4],
410    }
411
412    impl<'a, 'lend> Lending<'lend> for Child<'a> {
413        type Lend = &'lend [i32; 4];
414    }
415
416    impl<'a> Lender for Child<'a> {
417        crate::check_covariance!();
418        fn next(&mut self) -> Option<Lend<'_, Self>> {
419            Some(self.array_ref)
420        }
421    }
422
423    // This test will fail if FlattenCompat stores L instead of Box<L>. In that
424    // case, when Flatten<Parent> is moved, the array inside Parent is moved,
425    // too, but FlattenCompat.inner will still contain a Child holding a
426    // reference to the previous location.
427    #[test]
428    fn test_flatten() {
429        let lender = Parent([0, 1, 2, 3]);
430        let mut flatten = lender.flatten();
431        flatten.next();
432        moved_flatten(flatten);
433    }
434
435    fn moved_flatten(mut flatten: Flatten<Parent>) {
436        let next_array_ref = flatten.next().unwrap() as *const _;
437        let array_ref = &flatten.inner.lender.0 as *const _;
438        assert_eq!(
439            next_array_ref, array_ref,
440            "Array references returned by the flattened lender should refer to the array in the parent lender"
441        );
442    }
443
444    #[test]
445    fn test_flatmap_empty() {
446        use crate::traits::IteratorExt;
447
448        let mut l = [1, 0, 2]
449            .into_iter()
450            .into_lender()
451            // SAFETY: closure returns an owned lender (trivially covariant).
452            .flat_map(unsafe { crate::Covar::__new(|n: i32| (0..n).into_lender()) });
453        assert_eq!(l.next(), Some(0));
454        assert_eq!(l.next(), Some(0));
455        assert_eq!(l.next(), Some(1));
456        assert_eq!(l.next(), None);
457        assert_eq!(l.next(), None);
458    }
459}