snix_eval/value/
attrs.rs

1//! This module implements Nix attribute sets. They have flexible
2//! backing implementations, as they are used in very versatile
3//! use-cases that are all exposed the same way in the language
4//! surface.
5//!
6//! Due to this, construction and management of attribute sets has
7//! some peculiarities that are encapsulated within this module.
8use std::borrow::Borrow;
9use std::collections::{BTreeMap, hash_map};
10use std::iter::FromIterator;
11use std::rc::Rc;
12use std::sync::LazyLock;
13
14use bstr::{BStr, ByteSlice};
15use itertools::Itertools as _;
16use rustc_hash::FxHashMap;
17use serde::Deserialize;
18use serde::de::{Deserializer, Error, Visitor};
19
20use super::TotalDisplay;
21use super::Value;
22use super::string::NixString;
23use super::thunk::ThunkSet;
24use crate::CatchableErrorKind;
25use crate::errors::ErrorKind;
26
27static NAME: LazyLock<NixString> = LazyLock::new(|| "name".into());
28static VALUE: LazyLock<NixString> = LazyLock::new(|| "value".into());
29
30#[cfg(test)]
31mod tests;
32
33#[derive(Clone, Debug, Deserialize, Default)]
34pub(super) enum AttrsRep {
35    #[default]
36    Empty,
37
38    Map(FxHashMap<NixString, Value>),
39
40    /// Warning: this represents a **two**-attribute attrset, with
41    /// attribute names "name" and "value", like `{name="foo";
42    /// value="bar";}`, *not* `{foo="bar";}`!
43    KV {
44        name: Value,
45        value: Value,
46    },
47}
48
49impl AttrsRep {
50    fn select(&self, key: &BStr) -> Option<&Value> {
51        match self {
52            AttrsRep::Empty => None,
53
54            AttrsRep::KV { name, value } => match &**key {
55                b"name" => Some(name),
56                b"value" => Some(value),
57                _ => None,
58            },
59
60            AttrsRep::Map(map) => map.get(key),
61        }
62    }
63
64    fn contains(&self, key: &BStr) -> bool {
65        match self {
66            AttrsRep::Empty => false,
67            AttrsRep::KV { .. } => key == "name" || key == "value",
68            AttrsRep::Map(map) => map.contains_key(key),
69        }
70    }
71}
72
73#[repr(transparent)]
74#[derive(Clone, Debug, Default)]
75pub struct NixAttrs(pub(super) Rc<AttrsRep>);
76
77impl From<AttrsRep> for NixAttrs {
78    fn from(rep: AttrsRep) -> Self {
79        NixAttrs(Rc::new(rep))
80    }
81}
82
83impl<K, V> FromIterator<(K, V)> for NixAttrs
84where
85    NixString: From<K>,
86    Value: From<V>,
87{
88    fn from_iter<T>(iter: T) -> NixAttrs
89    where
90        T: IntoIterator<Item = (K, V)>,
91    {
92        AttrsRep::Map(
93            iter.into_iter()
94                .map(|(k, v)| (k.into(), v.into()))
95                .collect(),
96        )
97        .into()
98    }
99}
100
101impl From<BTreeMap<NixString, Value>> for NixAttrs {
102    fn from(map: BTreeMap<NixString, Value>) -> Self {
103        AttrsRep::Map(map.into_iter().collect()).into()
104    }
105}
106
107impl From<FxHashMap<NixString, Value>> for NixAttrs {
108    fn from(map: FxHashMap<NixString, Value>) -> Self {
109        AttrsRep::Map(map).into()
110    }
111}
112
113impl TotalDisplay for NixAttrs {
114    fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
115        if self.is_derivation() {
116            write!(f, "«derivation")?;
117            // We cant use the default TotalDisplay implementation, because nix doesn't put quotes
118            // around the path here.
119            if let Some(Value::Thunk(p)) = self.select("drvPath")
120                && p.is_forced()
121                && let Ok(drv) = p.value().to_contextful_str()
122            {
123                write!(f, " {}", drv.to_str_lossy())?;
124            };
125            return write!(f, "»");
126        }
127
128        f.write_str("{ ")?;
129
130        for (name, value) in self.iter_sorted() {
131            write!(f, "{} = ", name.ident_str())?;
132            value.total_fmt(f, set)?;
133            f.write_str("; ")?;
134        }
135
136        f.write_str("}")
137    }
138}
139
140impl<'de> Deserialize<'de> for NixAttrs {
141    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
142    where
143        D: Deserializer<'de>,
144    {
145        struct MapVisitor;
146
147        impl<'de> Visitor<'de> for MapVisitor {
148            type Value = NixAttrs;
149
150            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
151                formatter.write_str("a valid Nix attribute set")
152            }
153
154            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
155            where
156                A: serde::de::MapAccess<'de>,
157            {
158                let mut stack_array = Vec::with_capacity(map.size_hint().unwrap_or(0) * 2);
159
160                while let Some((key, value)) = map.next_entry()? {
161                    stack_array.push(key);
162                    stack_array.push(value);
163                }
164
165                Ok(NixAttrs::construct(stack_array.len() / 2, stack_array)
166                    .map_err(A::Error::custom)?
167                    .expect("Catchable values are unreachable here"))
168            }
169        }
170
171        deserializer.deserialize_map(MapVisitor)
172    }
173}
174
175impl NixAttrs {
176    pub fn empty() -> Self {
177        AttrsRep::Empty.into()
178    }
179
180    /// Compare two attribute sets by pointer equality. Only makes
181    /// sense for some attribute set representations, i.e. returning
182    /// `false` does not mean that the two attribute sets do not have
183    /// equal *content*.
184    pub fn ptr_eq(&self, other: &Self) -> bool {
185        Rc::ptr_eq(&self.0, &other.0)
186    }
187
188    /// Return an attribute set containing the merge of the two
189    /// provided sets. Keys from the `other` set have precedence.
190    pub fn update(self, other: Self) -> Self {
191        // Short-circuit on some optimal cases:
192        match (self.0.as_ref(), other.0.as_ref()) {
193            (AttrsRep::Empty, AttrsRep::Empty) => return self,
194            (AttrsRep::Empty, _) => return other,
195            (_, AttrsRep::Empty) => return self,
196            (AttrsRep::KV { .. }, AttrsRep::KV { .. }) => return other,
197
198            // Explicitly handle all branches instead of falling
199            // through, to ensure that we get at least some compiler
200            // errors if variants are modified.
201            (AttrsRep::Map(_), AttrsRep::Map(_))
202            | (AttrsRep::Map(_), AttrsRep::KV { .. })
203            | (AttrsRep::KV { .. }, AttrsRep::Map(_)) => {}
204        };
205
206        // Slightly more advanced, but still optimised updates
207        match (Rc::unwrap_or_clone(self.0), Rc::unwrap_or_clone(other.0)) {
208            (AttrsRep::Map(mut m), AttrsRep::KV { name, value }) => {
209                m.insert(NAME.clone(), name);
210                m.insert(VALUE.clone(), value);
211                AttrsRep::Map(m).into()
212            }
213
214            (AttrsRep::KV { name, value }, AttrsRep::Map(mut m)) => {
215                match m.entry(NAME.clone()) {
216                    hash_map::Entry::Vacant(e) => {
217                        e.insert(name);
218                    }
219
220                    hash_map::Entry::Occupied(_) => { /* name from `m` has precedence */ }
221                };
222
223                match m.entry(VALUE.clone()) {
224                    hash_map::Entry::Vacant(e) => {
225                        e.insert(value);
226                    }
227
228                    hash_map::Entry::Occupied(_) => { /* value from `m` has precedence */ }
229                };
230
231                AttrsRep::Map(m).into()
232            }
233
234            // Plain merge of maps.
235            (AttrsRep::Map(mut m1), AttrsRep::Map(mut m2)) => {
236                let map = if m1.len() >= m2.len() {
237                    m1.extend(m2);
238                    m1
239                } else {
240                    for (key, val) in m1.into_iter() {
241                        m2.entry(key).or_insert(val);
242                    }
243                    m2
244                };
245                AttrsRep::Map(map).into()
246            }
247
248            // Cases handled above by the borrowing match:
249            _ => unreachable!(),
250        }
251    }
252
253    /// Return the number of key-value entries in an attrset.
254    pub fn len(&self) -> usize {
255        match self.0.as_ref() {
256            AttrsRep::Map(map) => map.len(),
257            AttrsRep::Empty => 0,
258            AttrsRep::KV { .. } => 2,
259        }
260    }
261
262    pub fn is_empty(&self) -> bool {
263        match self.0.as_ref() {
264            AttrsRep::Map(map) => map.is_empty(),
265            AttrsRep::Empty => true,
266            AttrsRep::KV { .. } => false,
267        }
268    }
269
270    /// Select a value from an attribute set by key.
271    pub fn select<K>(&self, key: &K) -> Option<&Value>
272    where
273        K: Borrow<BStr> + ?Sized,
274    {
275        self.0.select(key.borrow())
276    }
277
278    /// Select a required value from an attribute set by key, return
279    /// an `AttributeNotFound` error if it is missing.
280    pub fn select_required<K>(&self, key: &K) -> Result<&Value, ErrorKind>
281    where
282        K: Borrow<BStr> + ?Sized,
283    {
284        self.select(key)
285            .ok_or_else(|| ErrorKind::AttributeNotFound {
286                name: key.borrow().to_string(),
287            })
288    }
289
290    pub fn contains<'a, K: 'a>(&self, key: K) -> bool
291    where
292        &'a BStr: From<K>,
293    {
294        self.0.contains(key.into())
295    }
296
297    /// Construct an iterator over all the key-value pairs in the attribute set.
298    #[allow(clippy::needless_lifetimes)]
299    pub fn iter<'a>(&'a self) -> Iter<KeyValue<'a>> {
300        Iter(match &self.0.as_ref() {
301            AttrsRep::Map(map) => KeyValue::Map(map.iter()),
302            AttrsRep::Empty => KeyValue::Empty,
303
304            AttrsRep::KV { name, value } => KeyValue::KV {
305                name,
306                value,
307                at: IterKV::default(),
308            },
309        })
310    }
311
312    /// Same as iter(), but marks call sites which rely on the
313    /// iteration being lexicographic.
314    pub fn iter_sorted(&self) -> Iter<KeyValue<'_>> {
315        Iter(match self.0.as_ref() {
316            AttrsRep::Empty => KeyValue::Empty,
317            AttrsRep::Map(map) => {
318                let sorted = map.iter().sorted_by_key(|x| x.0);
319                KeyValue::Sorted(sorted)
320            }
321            AttrsRep::KV { name, value } => KeyValue::KV {
322                name,
323                value,
324                at: IterKV::default(),
325            },
326        })
327    }
328
329    /// Same as [IntoIterator::into_iter], but marks call sites which rely on the
330    /// iteration being lexicographic.
331    pub fn into_iter_sorted(self) -> OwnedAttrsIterator {
332        let iter = match Rc::<AttrsRep>::try_unwrap(self.0) {
333            Ok(attrs) => match attrs {
334                AttrsRep::Empty => IntoIterRepr::Empty,
335                AttrsRep::Map(map) => {
336                    IntoIterRepr::Finite(map.into_iter().sorted_by(|(a, _), (b, _)| a.cmp(b)))
337                }
338                AttrsRep::KV { name, value } => IntoIterRepr::Finite(
339                    vec![(NAME.clone(), name), (VALUE.clone(), value)].into_iter(),
340                ),
341            },
342            Err(rc) => match rc.as_ref() {
343                AttrsRep::Empty => IntoIterRepr::Empty,
344                AttrsRep::Map(map) => IntoIterRepr::Finite(
345                    map.iter()
346                        .map(|(k, v)| (k.clone(), v.clone()))
347                        .sorted_by(|(a, _), (b, _)| a.cmp(b)),
348                ),
349                AttrsRep::KV { name, value } => IntoIterRepr::Finite(
350                    vec![(NAME.clone(), name.clone()), (VALUE.clone(), value.clone())].into_iter(),
351                ),
352            },
353        };
354        OwnedAttrsIterator(iter)
355    }
356
357    /// Construct an iterator over all the keys of the attribute set
358    pub fn keys(&self) -> Keys<'_> {
359        Keys(match self.0.as_ref() {
360            AttrsRep::Empty => KeysInner::Empty,
361            AttrsRep::KV { .. } => KeysInner::KV(IterKV::default()),
362
363            // TODO(tazjin): only sort when required, not always.
364            AttrsRep::Map(m) => KeysInner::Map(m.keys()),
365        })
366    }
367
368    /// Same as [Self::keys], but marks call sites which rely on the
369    /// iteration being lexicographic.
370    pub fn keys_sorted(&self) -> Keys<'_> {
371        Keys(match self.0.as_ref() {
372            AttrsRep::Map(map) => KeysInner::Sorted(map.keys().sorted()),
373            AttrsRep::Empty => KeysInner::Empty,
374            AttrsRep::KV { .. } => KeysInner::KV(IterKV::default()),
375        })
376    }
377
378    /// Implement construction logic of an attribute set, to encapsulate
379    /// logic about attribute set optimisations inside of this module.
380    pub fn construct(
381        count: usize,
382        mut stack_slice: Vec<Value>,
383    ) -> Result<Result<Self, CatchableErrorKind>, ErrorKind> {
384        debug_assert!(
385            stack_slice.len() == count * 2,
386            "construct_attrs called with count == {}, but slice.len() == {}",
387            count,
388            stack_slice.len(),
389        );
390
391        // Optimisation: Empty attribute set
392        if count == 0 {
393            return Ok(Ok(AttrsRep::Empty.into()));
394        }
395
396        // Optimisation: KV pattern
397        if count == 2
398            && let Some(kv) = attempt_optimise_kv(&mut stack_slice)
399        {
400            return Ok(Ok(kv));
401        }
402
403        let mut attrs_map = FxHashMap::with_capacity_and_hasher(count, rustc_hash::FxBuildHasher);
404
405        for _ in 0..count {
406            let value = stack_slice.pop().unwrap();
407            let key = stack_slice.pop().unwrap();
408
409            match key {
410                Value::String(ks) => set_attr(&mut attrs_map, ks, value)?,
411
412                Value::Null => {
413                    // This is in fact valid, but leads to the value
414                    // being ignored and nothing being set, i.e. `{
415                    // ${null} = 1; } => { }`.
416                    continue;
417                }
418
419                Value::Catchable(err) => return Ok(Err(*err)),
420
421                other => return Err(ErrorKind::InvalidAttributeName(other)),
422            }
423        }
424
425        Ok(Ok(AttrsRep::Map(attrs_map).into()))
426    }
427
428    /// Construct an optimized "KV"-style attribute set given the value for the
429    /// `"name"` key, and the value for the `"value"` key
430    pub(crate) fn from_kv(name: Value, value: Value) -> Self {
431        AttrsRep::KV { name, value }.into()
432    }
433
434    /// Calculate the intersection of the attribute sets.
435    /// The right side value is used when the keys match.
436    pub(crate) fn intersect(&self, other: &Self) -> NixAttrs {
437        match (self.0.as_ref(), other.0.as_ref()) {
438            (AttrsRep::Empty, _) | (_, AttrsRep::Empty) => AttrsRep::Empty.into(),
439            (AttrsRep::Map(lhs), AttrsRep::Map(rhs)) => {
440                let mut out = FxHashMap::with_capacity_and_hasher(
441                    std::cmp::min(lhs.len(), rhs.len()),
442                    rustc_hash::FxBuildHasher,
443                );
444                if lhs.len() < rhs.len() {
445                    for key in lhs.keys() {
446                        if let Some(val) = rhs.get(key) {
447                            out.insert(key.clone(), val.clone());
448                        }
449                    }
450                } else {
451                    for (key, val) in rhs.iter() {
452                        if lhs.contains_key(key) {
453                            out.insert(key.clone(), val.clone());
454                        }
455                    }
456                };
457                out.into()
458            }
459            (AttrsRep::Map(map), AttrsRep::KV { name, value }) => {
460                let mut out = FxHashMap::with_capacity_and_hasher(2, rustc_hash::FxBuildHasher);
461                if map.contains_key(NAME.as_bstr()) {
462                    out.insert(NAME.clone(), name.clone());
463                }
464                if map.contains_key(VALUE.as_bstr()) {
465                    out.insert(VALUE.clone(), value.clone());
466                }
467
468                if out.is_empty() {
469                    NixAttrs::empty()
470                } else {
471                    out.into()
472                }
473            }
474            (AttrsRep::KV { .. }, AttrsRep::Map(map)) => {
475                let mut out = FxHashMap::with_capacity_and_hasher(2, rustc_hash::FxBuildHasher);
476                if let Some(name) = map.get(NAME.as_bstr()) {
477                    out.insert(NAME.clone(), name.clone());
478                }
479                if let Some(value) = map.get(VALUE.as_bstr()) {
480                    out.insert(VALUE.clone(), value.clone());
481                }
482
483                if out.is_empty() {
484                    NixAttrs::empty()
485                } else {
486                    out.into()
487                }
488            }
489            (AttrsRep::KV { .. }, AttrsRep::KV { .. }) => other.clone(),
490        }
491    }
492
493    /// Returns whether this attr set is a derivation.
494    pub fn is_derivation(&self) -> bool {
495        let Some(Value::String(kind)) = self.select("type") else {
496            return false;
497        };
498        *kind == "derivation"
499    }
500}
501
502impl IntoIterator for NixAttrs {
503    type Item = (NixString, Value);
504    type IntoIter = OwnedAttrsIterator;
505
506    fn into_iter(self) -> Self::IntoIter {
507        match Rc::unwrap_or_clone(self.0) {
508            AttrsRep::Empty => OwnedAttrsIterator(IntoIterRepr::Empty),
509            AttrsRep::KV { name, value } => OwnedAttrsIterator(IntoIterRepr::Finite(
510                vec![(NAME.clone(), name), (VALUE.clone(), value)].into_iter(),
511            )),
512            AttrsRep::Map(map) => OwnedAttrsIterator(IntoIterRepr::Map(map.into_iter())),
513        }
514    }
515}
516
517/// In Nix, name/value attribute pairs are frequently constructed from
518/// literals. This particular case should avoid allocation of a map,
519/// additional heap values etc. and use the optimised `KV` variant
520/// instead.
521///
522/// ```norust
523/// `slice` is the top of the stack from which the attrset is being
524/// constructed, e.g.
525///
526///   slice: [ "value" 5 "name" "foo" ]
527///   index:   0       1 2      3
528///   stack:   3       2 1      0
529/// ```
530fn attempt_optimise_kv(slice: &mut [Value]) -> Option<NixAttrs> {
531    let (name_idx, value_idx) = {
532        match (&slice[2], &slice[0]) {
533            (Value::String(s1), Value::String(s2)) if (*s1 == *NAME && *s2 == *VALUE) => (3, 1),
534            (Value::String(s1), Value::String(s2)) if (*s1 == *VALUE && *s2 == *NAME) => (1, 3),
535
536            // Technically this branch lets type errors pass,
537            // but they will be caught during normal attribute
538            // set construction instead.
539            _ => return None,
540        }
541    };
542
543    Some(NixAttrs::from_kv(
544        slice[name_idx].clone(),
545        slice[value_idx].clone(),
546    ))
547}
548
549/// Set an attribute on an in-construction attribute set, while
550/// checking against duplicate keys.
551fn set_attr(
552    map: &mut FxHashMap<NixString, Value>,
553    key: NixString,
554    value: Value,
555) -> Result<(), ErrorKind> {
556    match map.entry(key) {
557        hash_map::Entry::Occupied(entry) => Err(ErrorKind::DuplicateAttrsKey {
558            key: entry.key().to_string(),
559        }),
560
561        hash_map::Entry::Vacant(entry) => {
562            entry.insert(value);
563            Ok(())
564        }
565    }
566}
567
568/// Internal helper type to track the iteration status of an iterator
569/// over the name/value representation.
570#[derive(Debug, Default)]
571pub enum IterKV {
572    #[default]
573    Name,
574    Value,
575    Done,
576}
577
578impl IterKV {
579    fn next(&mut self) {
580        match *self {
581            Self::Name => *self = Self::Value,
582            Self::Value => *self = Self::Done,
583            Self::Done => {}
584        }
585    }
586}
587
588/// Iterator representation over the keys *and* values of an attribute
589/// set.
590pub enum KeyValue<'a> {
591    Empty,
592
593    KV {
594        name: &'a Value,
595        value: &'a Value,
596        at: IterKV,
597    },
598
599    Map(hash_map::Iter<'a, NixString, Value>),
600
601    Sorted(std::vec::IntoIter<(&'a NixString, &'a Value)>),
602}
603
604/// Iterator over a Nix attribute set.
605// This wrapper type exists to make the inner "raw" iterator
606// inaccessible.
607#[repr(transparent)]
608pub struct Iter<T>(T);
609
610impl<'a> Iterator for Iter<KeyValue<'a>> {
611    type Item = (&'a NixString, &'a Value);
612
613    fn next(&mut self) -> Option<Self::Item> {
614        match &mut self.0 {
615            KeyValue::Map(inner) => inner.next(),
616            KeyValue::Empty => None,
617            KeyValue::KV { name, value, at } => match at {
618                IterKV::Name => {
619                    at.next();
620                    Some((&NAME, name))
621                }
622
623                IterKV::Value => {
624                    at.next();
625                    Some((&VALUE, value))
626                }
627
628                IterKV::Done => None,
629            },
630            KeyValue::Sorted(inner) => inner.next(),
631        }
632    }
633}
634
635impl ExactSizeIterator for Iter<KeyValue<'_>> {
636    fn len(&self) -> usize {
637        match &self.0 {
638            KeyValue::Empty => 0,
639            KeyValue::KV { .. } => 2,
640            KeyValue::Map(inner) => inner.len(),
641            KeyValue::Sorted(inner) => inner.len(),
642        }
643    }
644}
645
646enum KeysInner<'a> {
647    Empty,
648    KV(IterKV),
649    Map(hash_map::Keys<'a, NixString, Value>),
650    Sorted(std::vec::IntoIter<&'a NixString>),
651}
652
653pub struct Keys<'a>(KeysInner<'a>);
654
655impl<'a> Iterator for Keys<'a> {
656    type Item = &'a NixString;
657
658    fn next(&mut self) -> Option<Self::Item> {
659        match &mut self.0 {
660            KeysInner::Empty => None,
661            KeysInner::KV(at @ IterKV::Name) => {
662                at.next();
663                Some(&NAME)
664            }
665            KeysInner::KV(at @ IterKV::Value) => {
666                at.next();
667                Some(&VALUE)
668            }
669            KeysInner::KV(IterKV::Done) => None,
670            KeysInner::Map(m) => m.next(),
671            KeysInner::Sorted(v) => v.next(),
672        }
673    }
674}
675
676impl<'a> IntoIterator for &'a NixAttrs {
677    type Item = (&'a NixString, &'a Value);
678
679    type IntoIter = Iter<KeyValue<'a>>;
680
681    fn into_iter(self) -> Self::IntoIter {
682        self.iter()
683    }
684}
685
686impl ExactSizeIterator for Keys<'_> {
687    fn len(&self) -> usize {
688        match &self.0 {
689            KeysInner::Empty => 0,
690            KeysInner::KV(_) => 2,
691            KeysInner::Map(m) => m.len(),
692            KeysInner::Sorted(v) => v.len(),
693        }
694    }
695}
696
697/// Internal representation of an owning attrset iterator
698pub enum IntoIterRepr {
699    Empty,
700    Finite(std::vec::IntoIter<(NixString, Value)>),
701    Map(hash_map::IntoIter<NixString, Value>),
702}
703
704/// Wrapper type which hides the internal implementation details from
705/// users.
706#[repr(transparent)]
707pub struct OwnedAttrsIterator(IntoIterRepr);
708
709impl Iterator for OwnedAttrsIterator {
710    type Item = (NixString, Value);
711
712    fn next(&mut self) -> Option<Self::Item> {
713        match &mut self.0 {
714            IntoIterRepr::Empty => None,
715            IntoIterRepr::Finite(inner) => inner.next(),
716            IntoIterRepr::Map(m) => m.next(),
717        }
718    }
719}
720
721impl ExactSizeIterator for OwnedAttrsIterator {
722    fn len(&self) -> usize {
723        match &self.0 {
724            IntoIterRepr::Empty => 0,
725            IntoIterRepr::Finite(inner) => inner.len(),
726            IntoIterRepr::Map(inner) => inner.len(),
727        }
728    }
729}
730
731impl DoubleEndedIterator for OwnedAttrsIterator {
732    fn next_back(&mut self) -> Option<Self::Item> {
733        match &mut self.0 {
734            IntoIterRepr::Empty => None,
735            IntoIterRepr::Finite(inner) => inner.next_back(),
736            // hashmaps have arbitary iteration order, so reversing it would not make a difference
737            IntoIterRepr::Map(inner) => inner.next(),
738        }
739    }
740}