Skip to main content

miniextendr_api/altrep_data/iter/
state.rs

1//! Core iterator-backed ALTREP data adaptors.
2//!
3//! Provides `IterState<I, T>` (the shared lazy-caching state machine) and
4//! data-adaptor types for the integer/real/logical/raw ALTREP families:
5//! `IterIntData`, `IterRealData`, `IterLogicalData`, `IterRawData`. The
6//! string/complex adaptors live in `super::coerce`; the rooted list adaptor
7//! lives in `super::list`.
8//!
9//! See the iterator-adaptor section in the [`altrep_data`](crate::altrep_data)
10//! module docs for how to expose
11//! these adaptors to R: they implement only the data-level traits
12//! ([`AltrepLen`] + `Alt*Data`) and must be wrapped in a concrete
13//! `#[derive(Altrep*)]` + `#[altrep(manual)]` struct to back a live ALTREP
14//! vector.
15
16use super::macros::define_iter_data;
17use std::cell::RefCell;
18use std::sync::OnceLock;
19
20use crate::altrep_data::{
21    AltIntegerData, AltLogicalData, AltRawData, AltRealData, AltrepLen, Logical,
22};
23
24/// Core state for iterator-backed ALTREP vectors.
25///
26/// Provides lazy element generation with caching for random-access semantics.
27/// Iterator elements are cached as they're accessed, enabling repeatable reads.
28///
29/// # Type Parameters
30///
31/// - `I`: The iterator type (must be `ExactSizeIterator` or provide explicit length)
32/// - `T`: The element type produced by the iterator
33///
34/// # Design
35///
36/// - **Lazy:** Elements generated on-demand via `elt(i)`
37/// - **Cached:** Once generated, elements stored in cache for repeat access
38/// - **Materializable:** Can be fully materialized for `Dataptr` or serialization
39/// - **Borrow-checked:** Uses `RefCell` for interior mutability on the R thread
40#[derive(#[automatically_derived]
impl<I: ::core::clone::Clone, T: ::core::clone::Clone> ::core::clone::Clone
    for IterState<I, T> {
    #[inline]
    fn clone(&self) -> Self {
        IterState {
            len: ::core::clone::Clone::clone(&self.len),
            iter: ::core::clone::Clone::clone(&self.iter),
            cache: ::core::clone::Clone::clone(&self.cache),
            materialized: ::core::clone::Clone::clone(&self.materialized),
        }
    }
}Clone)]
41pub struct IterState<I, T> {
42    /// Vector length (from `ExactSizeIterator::len()` or explicit)
43    len: usize,
44    /// Iterator state (consumed as we advance)
45    iter: RefCell<Option<I>>,
46    /// Cache of generated elements (prefix of the vector)
47    cache: RefCell<Vec<T>>,
48    /// Full materialization (when all elements have been generated)
49    materialized: OnceLock<Vec<T>>,
50}
51
52impl<I, T> IterState<I, T>
53where
54    I: Iterator<Item = T>,
55{
56    /// Create a new iterator state with an explicit length.
57    ///
58    /// # Arguments
59    ///
60    /// - `iter`: The iterator to wrap
61    /// - `len`: The expected number of elements
62    ///
63    /// # Length Mismatch
64    ///
65    /// [`get_element`](Self::get_element) returns `None` for a missing index.
66    /// [`materialize_all`](Self::materialize_all) requires the complete declared
67    /// prefix and panics if the source ends early. Neither reads past `len`.
68    pub fn new(iter: I, len: usize) -> Self {
69        Self {
70            len,
71            iter: RefCell::new(Some(iter)),
72            cache: RefCell::new(Vec::with_capacity(len.min(1024))),
73            materialized: OnceLock::new(),
74        }
75    }
76
77    /// Restore a realized sequence without constructing or advancing an iterator.
78    ///
79    /// The iterator type is retained, but its state is absent: all reads use the
80    /// supplied values. This supports deserialization even when `I: !Default`.
81    pub fn from_values(values: Vec<T>) -> Self {
82        Self {
83            len: values.len(),
84            iter: RefCell::new(None),
85            cache: RefCell::new(Vec::new()),
86            materialized: OnceLock::from(values),
87        }
88    }
89
90    /// Ensure the element at index `i` is in the cache and return it by value.
91    ///
92    /// Advances the iterator as needed. Only works for `Copy` types.
93    ///
94    /// # Returns
95    ///
96    /// - `Some(T)` if element exists and has been generated
97    /// - `None` if index is out of bounds or iterator exhausted before reaching index `i`
98    pub fn get_element(&self, i: usize) -> Option<T>
99    where
100        T: Copy,
101    {
102        // Check bounds
103        if i >= self.len {
104            return None;
105        }
106
107        // If fully materialized, return from materialized vec
108        if let Some(vec) = self.materialized.get() {
109            return vec.get(i).copied();
110        }
111
112        // Otherwise, check cache and advance iterator if needed
113        let mut cache = self.cache.borrow_mut();
114
115        // Already in cache?
116        if i < cache.len() {
117            return Some(cache[i]);
118        }
119
120        // Need to advance iterator to index i
121        let mut iter_opt = self.iter.borrow_mut();
122        // Fill cache up to and including index i. A source that ends early is
123        // dropped at once: a non-fused iterator could otherwise yield again on
124        // a later call, giving an index that read as missing a value.
125        while cache.len() <= i {
126            match iter_opt.as_mut()?.next() {
127                Some(value) => cache.push(value),
128                None => {
129                    iter_opt.take();
130                    return None;
131                }
132            }
133        }
134
135        let value = cache[i];
136
137        // If we've generated the full vector via random-access, promote the cache
138        // to the materialized storage so `as_slice()` can expose it.
139        if cache.len() == self.len {
140            iter_opt.take();
141
142            let vec = std::mem::take(&mut *cache);
143            drop(cache);
144            drop(iter_opt);
145
146            let _ = self.materialized.set(vec);
147        }
148
149        Some(value)
150    }
151
152    /// Materialize exactly the declared prefix, including already cached elements.
153    ///
154    /// Does not request an element beyond `len`, including when `len` is zero
155    /// or the source is infinite. On success, [`Self::as_materialized`] returns
156    /// a slice whose length is exactly [`Self::len`].
157    ///
158    /// # Panics
159    ///
160    /// If the source ends before the declared length. The partial prefix stays
161    /// cached, but is never published as a complete buffer. A failed attempt
162    /// drops the iterator; another attempt cannot resume an exhausted source.
163    pub fn materialize_all(&self) -> &[T] {
164        match self.try_materialize_all() {
165            Ok(vec) => vec,
166            Err(cached) => {
    ::core::panicking::panic_fmt(format_args!("ALTREP iterator ended before element {0} of its declared length {1}",
            cached + 1, self.len));
}panic!(
167                "ALTREP iterator ended before element {} of its declared length {}",
168                cached + 1,
169                self.len
170            ),
171        }
172    }
173
174    /// [`Self::materialize_all`] without the panic: `Err` carries the number
175    /// of elements the source produced before it ended.
176    pub fn try_materialize_all(&self) -> Result<&[T], usize> {
177        if let Some(vec) = self.materialized.get() {
178            return Ok(vec);
179        }
180
181        let mut cache = self.cache.borrow_mut();
182        let mut iter_opt = self.iter.borrow_mut();
183        if let Some(mut iter) = iter_opt.take() {
184            while cache.len() < self.len {
185                let Some(value) = iter.next() else {
186                    break;
187                };
188                cache.push(value);
189            }
190        }
191        if cache.len() != self.len {
192            return Err(cache.len());
193        }
194
195        let vec = std::mem::take(&mut *cache);
196        drop(cache);
197        drop(iter_opt);
198        Ok(self.materialized.get_or_init(|| vec))
199    }
200
201    /// Get the materialized vector if all elements have been generated.
202    ///
203    /// Returns `None` if not yet fully materialized.
204    pub fn as_materialized(&self) -> Option<&[T]> {
205        self.materialized.get().map(|v| v.as_slice())
206    }
207
208    /// Get the current length.
209    pub fn len(&self) -> usize {
210        self.len
211    }
212
213    /// Check if the vector is empty.
214    pub fn is_empty(&self) -> bool {
215        self.len == 0
216    }
217}
218
219impl<I, T> IterState<I, T>
220where
221    I: ExactSizeIterator<Item = T>,
222{
223    /// Create a new iterator state from an `ExactSizeIterator`.
224    ///
225    /// The length is automatically determined from `iter.len()`.
226    pub fn from_exact_size(iter: I) -> Self {
227        let len = iter.len();
228        Self::new(iter, len)
229    }
230}
231
232#[doc = r" Iterator-backed integer vector data adaptor."]
#[doc = r""]
#[doc =
r" Wraps an iterator producing `i32` values and implements the data-level"]
#[doc =
r" traits ([`AltrepLen`] + [`AltIntegerData`]) for backing an ALTREP integer"]
#[doc =
r" vector. To expose it to R, wrap it in a `#[derive(AltrepInteger)]` +"]
#[doc =
r" `#[altrep(manual)]` struct (see the iterator module documentation)."]
#[doc = r""]
#[doc = r" # Example"]
#[doc = r""]
#[doc = r" ```ignore"]
#[doc = r" use miniextendr_api::altrep_data::IterIntData;"]
#[doc = r""]
#[doc = r" // Create from an iterator"]
#[doc = r" let data = IterIntData::from_iter((1..=10).map(|x| x * 2), 10);"]
#[doc = r" ```"]
pub struct IterIntData<I: Iterator<Item = i32>> {
    state: super::IterState<I, i32>,
}
#[automatically_derived]
impl<I: ::core::clone::Clone + Iterator<Item = i32>> ::core::clone::Clone for
    IterIntData<I> {
    #[inline]
    fn clone(&self) -> Self {
        IterIntData { state: ::core::clone::Clone::clone(&self.state) }
    }
}
impl<I: Iterator<Item = i32>> IterIntData<I> {
    /// Create a lazy adapter for exactly the declared prefix.
    ///
    /// The iterator is not advanced during construction.
    pub fn from_iter(iter: I, len: usize) -> Self {
        Self { state: super::IterState::new(iter, len) }
    }
}
impl<I: Iterator<Item = i32>> crate::altrep_data::AltrepSerialize for
    IterIntData<I> {
    fn serialized_state(&self) -> crate::SEXP {
        match self.state.try_materialize_all() {
            Ok(values) => crate::IntoR::into_sexp(values.to_vec()),
            Err(_) => crate::SEXP::null(),
        }
    }
    fn unserialize(state: crate::SEXP) -> Option<Self> {
        let values =
            <Vec<i32> as crate::TryFromSexp>::try_from_sexp(state).ok()?;
        Some(Self { state: super::IterState::from_values(values) })
    }
}
impl<I: ExactSizeIterator<Item = i32>> IterIntData<I> {
    /// Create a lazy adapter using the iterator's remaining length.
    pub fn from_exact_iter(iter: I) -> Self {
        let len = iter.len();
        Self::from_iter(iter, len)
    }
}
impl<I: Iterator<Item = i32>> crate::altrep_data::AltrepLen for IterIntData<I>
    {
    fn altrep_len(&self) -> usize { self.state.len() }
}define_iter_data! {
233/// Iterator-backed integer vector data adaptor.
234///
235/// Wraps an iterator producing `i32` values and implements the data-level
236/// traits ([`AltrepLen`] + [`AltIntegerData`]) for backing an ALTREP integer
237/// vector. To expose it to R, wrap it in a `#[derive(AltrepInteger)]` +
238/// `#[altrep(manual)]` struct (see the iterator module documentation).
239///
240/// # Example
241///
242/// ```ignore
243/// use miniextendr_api::altrep_data::IterIntData;
244///
245/// // Create from an iterator
246/// let data = IterIntData::from_iter((1..=10).map(|x| x * 2), 10);
247/// ```
248IterIntData, i32
249}
250
251impl<I: Iterator<Item = i32>> AltIntegerData for IterIntData<I> {
252    fn elt(&self, i: usize) -> i32 {
253        self.state
254            .get_element(i)
255            .unwrap_or(crate::altrep_traits::NA_INTEGER)
256    }
257
258    fn as_slice(&self) -> Option<&[i32]> {
259        self.state.as_materialized()
260    }
261}
262
263#[doc = r" Iterator-backed real (f64) vector data adaptor."]
#[doc = r""]
#[doc =
r" Wraps an iterator producing `f64` values and implements the data-level"]
#[doc =
r" traits ([`AltrepLen`] + [`AltRealData`]) for backing an ALTREP real vector."]
#[doc = r" To expose it to R, wrap it in a `#[derive(AltrepReal)]` +"]
#[doc =
r" `#[altrep(manual)]` struct (see the iterator module documentation)."]
pub struct IterRealData<I: Iterator<Item = f64>> {
    state: super::IterState<I, f64>,
}
#[automatically_derived]
impl<I: ::core::clone::Clone + Iterator<Item = f64>> ::core::clone::Clone for
    IterRealData<I> {
    #[inline]
    fn clone(&self) -> Self {
        IterRealData { state: ::core::clone::Clone::clone(&self.state) }
    }
}
impl<I: Iterator<Item = f64>> IterRealData<I> {
    /// Create a lazy adapter for exactly the declared prefix.
    ///
    /// The iterator is not advanced during construction.
    pub fn from_iter(iter: I, len: usize) -> Self {
        Self { state: super::IterState::new(iter, len) }
    }
}
impl<I: Iterator<Item = f64>> crate::altrep_data::AltrepSerialize for
    IterRealData<I> {
    fn serialized_state(&self) -> crate::SEXP {
        match self.state.try_materialize_all() {
            Ok(values) => crate::IntoR::into_sexp(values.to_vec()),
            Err(_) => crate::SEXP::null(),
        }
    }
    fn unserialize(state: crate::SEXP) -> Option<Self> {
        let values =
            <Vec<f64> as crate::TryFromSexp>::try_from_sexp(state).ok()?;
        Some(Self { state: super::IterState::from_values(values) })
    }
}
impl<I: ExactSizeIterator<Item = f64>> IterRealData<I> {
    /// Create a lazy adapter using the iterator's remaining length.
    pub fn from_exact_iter(iter: I) -> Self {
        let len = iter.len();
        Self::from_iter(iter, len)
    }
}
impl<I: Iterator<Item = f64>> crate::altrep_data::AltrepLen for
    IterRealData<I> {
    fn altrep_len(&self) -> usize { self.state.len() }
}define_iter_data! {
264/// Iterator-backed real (f64) vector data adaptor.
265///
266/// Wraps an iterator producing `f64` values and implements the data-level
267/// traits ([`AltrepLen`] + [`AltRealData`]) for backing an ALTREP real vector.
268/// To expose it to R, wrap it in a `#[derive(AltrepReal)]` +
269/// `#[altrep(manual)]` struct (see the iterator module documentation).
270IterRealData, f64
271}
272
273impl<I: Iterator<Item = f64>> AltRealData for IterRealData<I> {
274    fn elt(&self, i: usize) -> f64 {
275        self.state
276            .get_element(i)
277            .unwrap_or(crate::altrep_traits::NA_REAL)
278    }
279
280    fn as_slice(&self) -> Option<&[f64]> {
281        self.state.as_materialized()
282    }
283}
284
285#[doc = r" Iterator-backed logical vector data adaptor."]
#[doc = r""]
#[doc =
r" Wraps an iterator producing `bool` values and implements the data-level"]
#[doc =
r" traits ([`AltrepLen`] + [`AltLogicalData`]) for backing an ALTREP logical"]
#[doc =
r" vector. To expose it to R, wrap it in a `#[derive(AltrepLogical)]` +"]
#[doc =
r" `#[altrep(manual)]` struct (see the iterator module documentation)."]
pub struct IterLogicalData<I: Iterator<Item = bool>> {
    state: super::IterState<I, bool>,
}
#[automatically_derived]
impl<I: ::core::clone::Clone + Iterator<Item = bool>> ::core::clone::Clone for
    IterLogicalData<I> {
    #[inline]
    fn clone(&self) -> Self {
        IterLogicalData { state: ::core::clone::Clone::clone(&self.state) }
    }
}
impl<I: Iterator<Item = bool>> IterLogicalData<I> {
    /// Create a lazy adapter for exactly the declared prefix.
    ///
    /// The iterator is not advanced during construction.
    pub fn from_iter(iter: I, len: usize) -> Self {
        Self { state: super::IterState::new(iter, len) }
    }
}
impl<I: Iterator<Item = bool>> crate::altrep_data::AltrepSerialize for
    IterLogicalData<I> {
    fn serialized_state(&self) -> crate::SEXP {
        match self.state.try_materialize_all() {
            Ok(values) => crate::IntoR::into_sexp(values.to_vec()),
            Err(_) => crate::SEXP::null(),
        }
    }
    fn unserialize(state: crate::SEXP) -> Option<Self> {
        let values =
            <Vec<bool> as crate::TryFromSexp>::try_from_sexp(state).ok()?;
        Some(Self { state: super::IterState::from_values(values) })
    }
}
impl<I: ExactSizeIterator<Item = bool>> IterLogicalData<I> {
    /// Create a lazy adapter using the iterator's remaining length.
    pub fn from_exact_iter(iter: I) -> Self {
        let len = iter.len();
        Self::from_iter(iter, len)
    }
}
impl<I: Iterator<Item = bool>> crate::altrep_data::AltrepLen for
    IterLogicalData<I> {
    fn altrep_len(&self) -> usize { self.state.len() }
}define_iter_data! {
286/// Iterator-backed logical vector data adaptor.
287///
288/// Wraps an iterator producing `bool` values and implements the data-level
289/// traits ([`AltrepLen`] + [`AltLogicalData`]) for backing an ALTREP logical
290/// vector. To expose it to R, wrap it in a `#[derive(AltrepLogical)]` +
291/// `#[altrep(manual)]` struct (see the iterator module documentation).
292IterLogicalData, bool
293}
294
295impl<I: Iterator<Item = bool>> AltLogicalData for IterLogicalData<I> {
296    fn elt(&self, i: usize) -> Logical {
297        self.state
298            .get_element(i)
299            .map(Logical::from_bool)
300            .unwrap_or(Logical::Na)
301    }
302}
303
304#[doc = r" Iterator-backed raw (u8) vector data adaptor."]
#[doc = r""]
#[doc =
r" Wraps an iterator producing `u8` values and implements the data-level"]
#[doc =
r" traits ([`AltrepLen`] + [`AltRawData`]) for backing an ALTREP raw vector."]
#[doc = r" To expose it to R, wrap it in a `#[derive(AltrepRaw)]` +"]
#[doc =
r" `#[altrep(manual)]` struct (see the iterator module documentation)."]
pub struct IterRawData<I: Iterator<Item = u8>> {
    state: super::IterState<I, u8>,
}
#[automatically_derived]
impl<I: ::core::clone::Clone + Iterator<Item = u8>> ::core::clone::Clone for
    IterRawData<I> {
    #[inline]
    fn clone(&self) -> Self {
        IterRawData { state: ::core::clone::Clone::clone(&self.state) }
    }
}
impl<I: Iterator<Item = u8>> IterRawData<I> {
    /// Create a lazy adapter for exactly the declared prefix.
    ///
    /// The iterator is not advanced during construction.
    pub fn from_iter(iter: I, len: usize) -> Self {
        Self { state: super::IterState::new(iter, len) }
    }
}
impl<I: Iterator<Item = u8>> crate::altrep_data::AltrepSerialize for
    IterRawData<I> {
    fn serialized_state(&self) -> crate::SEXP {
        match self.state.try_materialize_all() {
            Ok(values) => crate::IntoR::into_sexp(values.to_vec()),
            Err(_) => crate::SEXP::null(),
        }
    }
    fn unserialize(state: crate::SEXP) -> Option<Self> {
        let values =
            <Vec<u8> as crate::TryFromSexp>::try_from_sexp(state).ok()?;
        Some(Self { state: super::IterState::from_values(values) })
    }
}
impl<I: ExactSizeIterator<Item = u8>> IterRawData<I> {
    /// Create a lazy adapter using the iterator's remaining length.
    pub fn from_exact_iter(iter: I) -> Self {
        let len = iter.len();
        Self::from_iter(iter, len)
    }
}
impl<I: Iterator<Item = u8>> crate::altrep_data::AltrepLen for IterRawData<I>
    {
    fn altrep_len(&self) -> usize { self.state.len() }
}define_iter_data! {
305/// Iterator-backed raw (u8) vector data adaptor.
306///
307/// Wraps an iterator producing `u8` values and implements the data-level
308/// traits ([`AltrepLen`] + [`AltRawData`]) for backing an ALTREP raw vector.
309/// To expose it to R, wrap it in a `#[derive(AltrepRaw)]` +
310/// `#[altrep(manual)]` struct (see the iterator module documentation).
311IterRawData, u8
312}
313
314impl<I: Iterator<Item = u8>> AltRawData for IterRawData<I> {
315    fn elt(&self, i: usize) -> u8 {
316        self.state.get_element(i).unwrap_or(0)
317    }
318
319    fn as_slice(&self) -> Option<&[u8]> {
320        self.state.as_materialized()
321    }
322}
323
324#[doc =
r" Iterator-backed logical data retaining R's native logical bit patterns."]
pub struct IterRLogicalData<I: Iterator<Item = crate::RLogical>> {
    state: super::IterState<I, crate::RLogical>,
}
#[automatically_derived]
impl<I: ::core::clone::Clone + Iterator<Item = crate::RLogical>>
    ::core::clone::Clone for IterRLogicalData<I> {
    #[inline]
    fn clone(&self) -> Self {
        IterRLogicalData { state: ::core::clone::Clone::clone(&self.state) }
    }
}
impl<I: Iterator<Item = crate::RLogical>> IterRLogicalData<I> {
    /// Create a lazy adapter for exactly the declared prefix.
    ///
    /// The iterator is not advanced during construction.
    pub fn from_iter(iter: I, len: usize) -> Self {
        Self { state: super::IterState::new(iter, len) }
    }
}
impl<I: Iterator<Item = crate::RLogical>> crate::altrep_data::AltrepSerialize
    for IterRLogicalData<I> {
    fn serialized_state(&self) -> crate::SEXP {
        match self.state.try_materialize_all() {
            Ok(values) => crate::IntoR::into_sexp(values.to_vec()),
            Err(_) => crate::SEXP::null(),
        }
    }
    fn unserialize(state: crate::SEXP) -> Option<Self> {
        let values =
            <Vec<crate::RLogical> as
                            crate::TryFromSexp>::try_from_sexp(state).ok()?;
        Some(Self { state: super::IterState::from_values(values) })
    }
}
impl<I: ExactSizeIterator<Item = crate::RLogical>> IterRLogicalData<I> {
    /// Create a lazy adapter using the iterator's remaining length.
    pub fn from_exact_iter(iter: I) -> Self {
        let len = iter.len();
        Self::from_iter(iter, len)
    }
}
impl<I: Iterator<Item = crate::RLogical>> crate::altrep_data::AltrepLen for
    IterRLogicalData<I> {
    fn altrep_len(&self) -> usize { self.state.len() }
}define_iter_data! {
325/// Iterator-backed logical data retaining R's native logical bit patterns.
326IterRLogicalData, crate::RLogical
327}
328
329impl<I: Iterator<Item = crate::RLogical>> AltLogicalData for IterRLogicalData<I> {
330    fn elt(&self, i: usize) -> Logical {
331        self.state
332            .get_element(i)
333            .map(Logical::from)
334            .unwrap_or(Logical::Na)
335    }
336
337    fn as_r_slice(&self) -> Option<&[i32]> {
338        let values = self.state.as_materialized()?;
339        // RLogical is transparent over i32 and accepts every bit pattern.
340        Some(unsafe { std::slice::from_raw_parts(values.as_ptr().cast(), values.len()) })
341    }
342
343    fn get_region(&self, start: usize, len: usize, out: &mut [i32]) -> usize {
344        crate::altrep_data::fill_region(start, len, self.altrep_len(), out, |i| {
345            self.state
346                .get_element(i)
347                .unwrap_or(crate::RLogical::NA)
348                .to_i32()
349        })
350    }
351}
352
353#[cfg(test)]
354mod tests {
355    use super::IterState;
356    use std::panic::{AssertUnwindSafe, catch_unwind};
357
358    #[test]
359    fn materialization_never_probes_past_the_declared_prefix() {
360        for len in [0, 3] {
361            let source = (0..len)
362                .map(|i| i.to_string())
363                .chain(std::iter::once_with(|| panic!("read beyond the prefix")));
364            let state = IterState::new(source, len);
365            let expected: Vec<_> = (0..len).map(|i| i.to_string()).collect();
366            assert_eq!(state.materialize_all(), expected);
367            assert_eq!(state.materialize_all(), expected);
368        }
369    }
370
371    #[test]
372    fn materialization_continues_after_a_cached_prefix() {
373        let source = (0..4).chain(std::iter::once_with(|| panic!("read beyond the prefix")));
374        let state = IterState::new(source, 4);
375        assert_eq!(state.get_element(1), Some(1));
376        assert_eq!(state.materialize_all(), [0, 1, 2, 3]);
377    }
378
379    #[test]
380    fn an_exhausted_source_is_never_polled_again() {
381        // Yields 0, then None, then 1 if polled again (not fused).
382        struct Flaky(u8);
383        impl Iterator for Flaky {
384            type Item = i32;
385            fn next(&mut self) -> Option<i32> {
386                self.0 += 1;
387                match self.0 {
388                    1 => Some(0),
389                    3 => Some(1),
390                    _ => None,
391                }
392            }
393        }
394        let state = IterState::new(Flaky(0), 2);
395        assert_eq!(state.get_element(1), None);
396        assert_eq!(state.get_element(1), None);
397        assert_eq!(state.get_element(0), Some(0));
398    }
399
400    #[test]
401    fn early_exhaustion_never_publishes_a_partial_buffer() {
402        let state = IterState::new(0..2, 3);
403        for _ in 0..2 {
404            assert!(catch_unwind(AssertUnwindSafe(|| state.materialize_all())).is_err());
405            assert!(state.as_materialized().is_none());
406            assert_eq!(state.get_element(0), Some(0));
407            assert_eq!(state.get_element(2), None);
408        }
409        assert_eq!(state.try_materialize_all(), Err(2));
410    }
411
412    #[test]
413    fn clone_preserves_a_partial_prefix_without_advancing_the_source() {
414        let original = IterState::new(10..20i32, 6);
415        assert_eq!(original.get_element(2), Some(12));
416        let cloned = original.clone();
417        assert!(original.as_materialized().is_none());
418        assert!(cloned.as_materialized().is_none());
419        assert_eq!(cloned.materialize_all(), &[10, 11, 12, 13, 14, 15]);
420        assert!(original.as_materialized().is_none());
421        assert_eq!(original.get_element(3), Some(13));
422        assert_eq!(original.materialize_all(), cloned.materialize_all());
423    }
424
425    #[test]
426    fn restored_values_need_no_iterator_constructor() {
427        type Source = std::iter::Take<std::iter::Repeat<i32>>;
428        let restored = IterState::<Source, i32>::from_values(vec![4, 5, 6]);
429        assert_eq!(restored.len(), 3);
430        assert_eq!(restored.get_element(1), Some(5));
431        assert_eq!(restored.materialize_all(), &[4, 5, 6]);
432    }
433}