Skip to main content

once_list2/
once_list.rs

1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use ::allocator_api2::alloc::{Allocator, Global};
16use ::allocator_api2::boxed::Box;
17use ::std::fmt::Debug;
18use ::std::hash::Hash;
19#[cfg(feature = "nightly")]
20use ::std::marker::Unsize;
21use ::std::ops::DerefMut;
22
23use crate::cache_mode::{CacheMode, NextSlot, NoCache, WithLen, WithTail, WithTailLen};
24use crate::cons::Cons;
25use crate::iter::{IntoIter, Iter, IterMut};
26
27/// A single linked list which behaves like [`std::cell::OnceCell`], but for multiple values.
28///
29/// This is a type alias of the internal implementation type. The default caching mode is `NoCache`.
30pub type OnceList<T, A = Global> = OnceListCore<T, A, NoCache>;
31
32/// A `OnceList` variant with tail caching enabled.
33pub type OnceListWithTail<T, A = Global> = OnceListCore<T, A, WithTail<T, A>>;
34
35/// A `OnceList` variant with length caching enabled (O(1) `len()`).
36pub type OnceListWithLen<T, A = Global> = OnceListCore<T, A, WithLen<T, A>>;
37
38/// A `OnceList` variant with both tail and length caching enabled.
39pub type OnceListWithTailLen<T, A = Global> = OnceListCore<T, A, WithTailLen<T, A>>;
40///
41/// # Usage
42///
43/// A simple example:
44///
45/// ```rust
46/// use once_list2::OnceList;
47///
48/// // Create a new empty list. Note that the variable is immutable.
49/// let list = OnceList::<i32>::new();
50///
51/// // You can push values to the list without the need for mutability.
52/// list.push(1);
53/// list.push(2);
54///
55/// // Or you can push multiple values at once.
56/// list.extend([3, 4, 5]);
57///
58/// // You can iterate over the list.
59/// assert_eq!(list.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3, 4, 5]);
60///
61/// // Some methods are mutable only.
62/// let mut list_mut = list;
63///
64/// // You can remove (take) a value from the list.
65/// let removed = list_mut.remove(|&x| x % 2 == 0);
66/// assert_eq!(removed, Some(2));
67/// assert_eq!(list_mut.iter().copied().collect::<Vec<_>>(), vec![1, 3, 4, 5]);
68/// ```
69///
70/// # Caching modes (optional)
71///
72/// You can choose a mode depending on what you want to optimize:
73///
74/// - **No cache (default)**:
75///   - `OnceList::<T>::new()`
76///   - `OnceList::<T, A>::new_in(alloc)`
77///   - `push_back()`: O(n), `len()`: O(n)
78///
79/// - **Len cache** (O(1) `len()`):
80///   - Type: `once_list2::OnceListWithLen<T, A>`
81///   - Constructors: `OnceListWithLen::<T>::new()` / `OnceListWithLen::<T, A>::new_in(alloc)`
82///
83/// - **Tail cache** (fast repeated tail inserts):
84///   - Type: `once_list2::OnceListWithTail<T, A>`
85///   - Constructors: `OnceListWithTail::<T>::new()` / `OnceListWithTail::<T, A>::new_in(alloc)`
86///   - Note: This mode caches the *next insertion slot* and speeds up operations that need to find
87///     the tail insertion point (e.g. `push_back()` / `extend()`), but it does not make `back()` O(1).
88///
89/// - **Tail + len cache**:
90///   - Type: `once_list2::OnceListWithTailLen<T, A>`
91///   - Constructors: `OnceListWithTailLen::<T>::new()` / `OnceListWithTailLen::<T, A>::new_in(alloc)`
92///
93/// These modes keep the same behavior guarantees (including the iterator observing newly pushed values).
94///
95/// # Unsized types support
96///
97/// You can use the [unsized types] like `str`, `[u8]` or `dyn Display` as the value type of the `OnceList`.
98///
99/// If you are using the stable rust compiler, you can only use the `dyn Any` type as the unsized type.
100/// (Strictly speaking, you can use ANY type as the unsized type, but you can't do any actual operations
101/// like pushing, removing, etc.)
102///
103/// In the nightly compiler and with the `nightly` feature enabled, the additional methods like `push_unsized`
104/// and `remove_unsized_as` become available:
105///
106/// ```rust
107/// # #[cfg(not(feature = "nightly"))]
108/// # fn main() {}
109/// # #[cfg(feature = "nightly")]
110/// # fn main() {
111/// // This code only works with the nightly compiler and the `nightly` feature enabled.
112///
113/// use once_list2::OnceList;
114///
115/// // Creating a `OnceList` for `[i32]`, the unsized type.
116/// let list = OnceList::<[i32]>::new();
117///
118/// list.push_unsized([1] /* A sized array type, `[i32; 1]`, can be coerced into [i32].*/);
119/// list.push_unsized([2, 3] /* Same for `[i32; 2] type. */);
120///
121/// // The normal methods like `iter` are available because it returns a reference to the value.
122/// assert_eq!(list.iter().nth(0).unwrap(), &[1]);
123/// assert_eq!(list.iter().nth(1).unwrap(), &[2, 3]);
124///
125/// let mut list_mut = list;
126///
127/// // `remove_unsized_as` method allows you to check the unsized value type and remove it.
128/// let removed: Option<[i32; 2]> = unsafe {
129///     list_mut.remove_unsized_as(|x| if x.len() == 2 {
130///         Some(x.try_into().unwrap())
131///     } else {
132///         None
133///     })
134/// };
135/// // The removed value is an array, not a slice!
136/// assert_eq!(removed, Some([2, 3]));
137/// # }
138/// ```
139/// [unsized types]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
140///
141/// # Note about docs
142///
143/// The method implementations live on [`OnceListCore`]. The user-facing type aliases like
144/// [`OnceList`] and [`OnceListWithTail`] point to this type.
145#[derive(Clone)]
146pub struct OnceListCore<T: ?Sized, A: Allocator = Global, C = NoCache> {
147    pub(crate) head_slot: NextSlot<T, A>,
148    pub(crate) alloc: A,
149    pub(crate) cache_mode: C,
150}
151
152// Per-mode `new()`/`new_in()` constructors.
153//
154// Note: These do not conflict with `OnceList::new()` because `OnceList` is a type alias
155// for `OnceListCore<_, _, NoCache>`, so the mode is fixed when calling `OnceList::new()`.
156
157impl<T: ?Sized> OnceListCore<T, Global, WithLen<T, Global>> {
158    pub fn new() -> Self {
159        Self {
160            head_slot: NextSlot::new(),
161            alloc: Global,
162            cache_mode: WithLen::new(),
163        }
164    }
165}
166
167impl<T: ?Sized, A: Allocator> OnceListCore<T, A, WithLen<T, A>> {
168    pub fn new_in(alloc: A) -> Self {
169        Self {
170            head_slot: NextSlot::new(),
171            alloc,
172            cache_mode: WithLen::new(),
173        }
174    }
175}
176
177impl<T: ?Sized> OnceListCore<T, Global, WithTail<T, Global>> {
178    pub fn new() -> Self {
179        Self {
180            head_slot: NextSlot::new(),
181            alloc: Global,
182            cache_mode: WithTail::new(),
183        }
184    }
185}
186
187impl<T: ?Sized, A: Allocator> OnceListCore<T, A, WithTail<T, A>> {
188    pub fn new_in(alloc: A) -> Self {
189        Self {
190            head_slot: NextSlot::new(),
191            alloc,
192            cache_mode: WithTail::new(),
193        }
194    }
195}
196
197impl<T: ?Sized> OnceListCore<T, Global, WithTailLen<T, Global>> {
198    pub fn new() -> Self {
199        Self {
200            head_slot: NextSlot::new(),
201            alloc: Global,
202            cache_mode: WithTailLen::new(),
203        }
204    }
205}
206
207impl<T: ?Sized, A: Allocator> OnceListCore<T, A, WithTailLen<T, A>> {
208    pub fn new_in(alloc: A) -> Self {
209        Self {
210            head_slot: NextSlot::new(),
211            alloc,
212            cache_mode: WithTailLen::new(),
213        }
214    }
215}
216
217impl<T: ?Sized> OnceListCore<T, Global, NoCache> {
218    /// Creates a new empty `OnceList`. This method does not allocate.
219    pub fn new() -> Self {
220        Self {
221            head_slot: NextSlot::new(),
222            alloc: Global,
223            cache_mode: NoCache,
224        }
225    }
226}
227
228impl<T: ?Sized, A: Allocator> OnceListCore<T, A, NoCache> {
229    /// Creates a new empty `OnceList` with the given allocator. This method does not allocate.
230    pub fn new_in(alloc: A) -> Self {
231        Self {
232            head_slot: NextSlot::new(),
233            alloc,
234            cache_mode: NoCache,
235        }
236    }
237}
238
239impl<T: ?Sized, A: Allocator, C> OnceListCore<T, A, C> {
240    /// Returns the number of values in the list.
241    ///
242    /// - O(1) if the current cache mode caches length
243    /// - O(n) otherwise
244    pub fn len(&self) -> usize
245    where
246        C: CacheMode<T, A>,
247    {
248        if let Some(n) = self.cache_mode.cached_len() {
249            return n;
250        }
251        self.iter().count()
252    }
253
254    /// Returns `true` if the list is empty.
255    pub fn is_empty(&self) -> bool {
256        self.head_slot.get().is_none()
257    }
258
259    /// Returns `true` if the list contains the value.
260    pub fn contains(&self, val: &T) -> bool
261    where
262        T: PartialEq,
263    {
264        self.iter().any(|v| v == val)
265    }
266
267    /// Returns the front value, if it exists.
268    pub fn front(&self) -> Option<&T> {
269        self.head_slot.get().map(|c| &c.val)
270    }
271
272    /// Returns a mutable reference to the front value, if it exists.
273    pub fn front_mut(&mut self) -> Option<&mut T> {
274        self.head_slot.get_mut().map(|c| &mut c.val)
275    }
276
277    /// Returns the back value, if it exists.
278    /// This method is O(n).
279    pub fn back(&self) -> Option<&T> {
280        let mut last_opt = None;
281        let mut next_cell = &self.head_slot;
282        while let Some(next_box) = next_cell.get() {
283            last_opt = Some(&next_box.val);
284            next_cell = &next_box.next;
285        }
286        last_opt
287    }
288
289    /// Returns a mutable reference to the back value, if it exists.
290    /// This method is O(n).
291    pub fn back_mut(&mut self) -> Option<&mut T> {
292        let mut last_opt = None;
293        let mut next_cell = &mut self.head_slot;
294        while let Some(next_box) = next_cell.get_mut() {
295            let next_cons = Box::deref_mut(next_box);
296            last_opt = Some(&mut next_cons.val);
297            next_cell = &mut next_cons.next;
298        }
299        last_opt
300    }
301
302    /// Returns the front value, if it exists.
303    ///
304    /// This is an alias of [`OnceListCore::front`].
305    pub fn first(&self) -> Option<&T> {
306        self.front()
307    }
308
309    /// Returns a mutable reference to the front value, if it exists.
310    ///
311    /// This is an alias of [`OnceListCore::front_mut`].
312    pub fn first_mut(&mut self) -> Option<&mut T> {
313        self.front_mut()
314    }
315
316    /// Returns the back value, if it exists.
317    ///
318    /// This is an alias of [`OnceListCore::back`].
319    pub fn last(&self) -> Option<&T> {
320        self.back()
321    }
322
323    /// Returns a mutable reference to the back value, if it exists.
324    ///
325    /// This is an alias of [`OnceListCore::back_mut`].
326    pub fn last_mut(&mut self) -> Option<&mut T> {
327        self.back_mut()
328    }
329
330    /// Returns an iterator over the `&T` references in the list.
331    pub fn iter(&self) -> Iter<'_, T, A> {
332        Iter::new(&self.head_slot)
333    }
334
335    /// Returns an iterator over the `&mut T` references in the list.
336    pub fn iter_mut(&mut self) -> IterMut<'_, T, A> {
337        IterMut::new(&mut self.head_slot)
338    }
339
340    /// Returns an allocator of this struct.
341    pub fn allocator(&self) -> &A {
342        &self.alloc
343    }
344}
345
346impl<T: ?Sized, A: Allocator, C> OnceListCore<T, A, C>
347where
348    C: CacheMode<T, A>,
349{
350    /// Clears the list, dropping all values.
351    pub fn clear(&mut self) {
352        self.head_slot = NextSlot::new();
353        self.cache_mode.on_clear();
354        self.cache_mode.on_structure_change();
355    }
356}
357
358impl<T: ?Sized, A: Allocator, C> OnceListCore<T, A, C>
359where
360    C: CacheMode<T, A>,
361{
362    /// Removes the first value in the list that matches the predicate, and returns the value as a boxed value.
363    ///
364    /// This method supports the unsized value type `T` as well.
365    ///
366    /// Note that even though this method returns a boxed value, the box is something re-allcoated.
367    /// So this method might not be efficient as you expect.
368    #[cfg(feature = "nightly")]
369    #[cfg_attr(feature = "nightly", doc(cfg(feature = "nightly")))]
370    pub fn remove_into_box<P>(&mut self, pred: P) -> Option<Box<T, A>>
371    where
372        P: FnMut(&T) -> bool,
373    {
374        self.remove_inner(pred, |boxed_cons| boxed_cons.box_into_inner_box())
375    }
376
377    /// Removes the first value in the list that matches the predicate, and returns the value.
378    ///
379    /// The predicate function `pred` should return `Some(&U)` if the value is found,
380    /// and the returned reference `&U` must be the same address as the value given in the `pred`.
381    ///
382    /// # Safety
383    /// This method is unsafe because it requires the predicate to return a reference to the same address as the value.
384    #[cfg(feature = "nightly")]
385    #[cfg_attr(feature = "nightly", doc(cfg(feature = "nightly")))]
386    pub unsafe fn remove_unsized_as<P, U>(&mut self, mut pred: P) -> Option<U>
387    where
388        P: FnMut(&T) -> Option<&U>,
389    {
390        use ::allocator_api2::alloc;
391        use ::std::cell::Cell;
392
393        let found_sized_ptr: Cell<Option<*const U>> = Cell::new(None);
394        self.remove_inner(
395            |val| {
396                if let Some(val) = pred(val) {
397                    found_sized_ptr.set(Some(val as *const U));
398                    true
399                } else {
400                    false
401                }
402            },
403            |boxed_cons| -> U {
404                // Given the boxed cons with the unsized value type `T`,
405                // and returns the sized type value `U` by value (i.e. out of the box).
406
407                // We are sure the `found_sized_ptr` is set when `remove_inner` calls this closure.
408                let found_sized_ptr: *const U = match found_sized_ptr.get() {
409                    Some(p) => p,
410                    None => unreachable!("remove_unsized_as: missing found pointer"),
411                };
412
413                let cons_layout = alloc::Layout::for_value::<Cons<T, T, A>>(&boxed_cons);
414                let (cons_ptr, alloc) = Box::into_non_null_with_allocator(boxed_cons);
415                let val_ptr = &unsafe { cons_ptr.as_ref() }.val as *const T;
416
417                // Double check the ptr returned by the `pred` is the same as the pointer we extracted from the cons.
418                debug_assert_eq!(val_ptr as *const U, found_sized_ptr);
419
420                // Load (memcpy) the value into the output variable.
421                let result = unsafe { ::std::ptr::read(val_ptr as *const U) };
422
423                // Free the cons memory.
424                unsafe { alloc.deallocate(cons_ptr.cast(), cons_layout) };
425
426                result
427            },
428        )
429    }
430
431    /// An inner implementeation for `remove_xxx` methods.
432    pub(crate) fn remove_inner<P, F, U>(&mut self, mut pred: P, mut f: F) -> Option<U>
433    where
434        P: FnMut(&T) -> bool,
435        F: FnMut(Box<Cons<T, T, A>, A>) -> U,
436    {
437        // Any structural change through `&mut self` invalidates the cached tail slot.
438        self.cache_mode.on_structure_change();
439
440        let mut next_cell = &mut self.head_slot;
441        while let Some(next_ref) = next_cell.get() {
442            if pred(&next_ref.val) {
443                // Safe because we are sure the `next_cell` value is set.
444                let Some(mut next_box) = next_cell.take() else {
445                    unreachable!("remove_inner: next_cell had value but take() returned None");
446                };
447
448                // reconnect the list
449                if let Some(next_next) = next_box.next.take() {
450                    let _ = next_cell.set(next_next);
451                }
452
453                self.cache_mode.on_remove_success();
454                return Some(f(next_box));
455            }
456            // Safe because we are sure the `next_cell` value is set.
457            let Some(next_box) = next_cell.get_mut() else {
458                unreachable!("remove_inner: next_cell had value but get_mut() returned None");
459            };
460            next_cell = &mut next_box.next;
461        }
462        None
463    }
464}
465
466impl<T: ?Sized, A: Allocator + Clone, C> OnceListCore<T, A, C>
467where
468    C: CacheMode<T, A>,
469{
470    /// An unsized version of the [`OnceList::push`] method.
471    ///
472    /// You can push a sized value to the list. For exaple, you can push `[i32; 3]` to the list of `[i32]`.
473    #[cfg(feature = "nightly")]
474    #[cfg_attr(feature = "nightly", doc(cfg(feature = "nightly")))]
475    pub fn push_unsized<U: Unsize<T>>(&self, val: U) -> &U {
476        let boxed_cons = Cons::new_boxed(val, self.alloc.clone());
477        self.push_inner(boxed_cons, |c| unsafe { &*(c as *const T as *const U) })
478    }
479
480    /// An inner implementation for the `push_xxx` methods.
481    pub(crate) fn push_inner<F, U>(&self, mut new_cons: Box<Cons<T, T, A>, A>, f: F) -> &U
482    where
483        F: FnOnce(&T) -> &U,
484    {
485        let mut next_cell = self.cache_mode.tail_slot_opt().unwrap_or(&self.head_slot);
486        loop {
487            match next_cell.try_insert2(new_cons) {
488                Ok(new_cons) => {
489                    self.cache_mode.on_push_success(&new_cons.next);
490                    return f(&new_cons.val);
491                }
492                Err((cur_cons, new_cons2)) => {
493                    next_cell = &cur_cons.next;
494                    new_cons = new_cons2;
495                }
496            }
497        }
498    }
499}
500
501impl<T, A: Allocator, C> OnceListCore<T, A, C>
502where
503    C: CacheMode<T, A>,
504{
505    /// Removes the front value from the list, and returns it.
506    ///
507    /// This method is O(1).
508    pub fn pop_front(&mut self) -> Option<T> {
509        self.remove(|_| true)
510    }
511
512    /// Find a first value in the list matches the predicate, remove that item from the list,
513    /// and then returns that value.
514    pub fn remove<P>(&mut self, mut pred: P) -> Option<T>
515    where
516        P: FnMut(&T) -> bool,
517    {
518        self.remove_inner(&mut pred, |boxed_cons| Box::into_inner(boxed_cons).val)
519    }
520}
521
522impl<T, A: Allocator + Clone, C> OnceListCore<T, A, C>
523where
524    C: CacheMode<T, A>,
525{
526    /// Appends a value to the back of the list, and returns the reference to that value.
527    ///
528    /// Note that this method takes `&self`, not `&mut self`.
529    pub fn push_back(&self, val: T) -> &T {
530        let boxed_cons = Box::new_in(Cons::new(val), A::clone(&self.alloc));
531        self.push_inner(boxed_cons, |c| c)
532    }
533
534    /// Appends a value to the list, and returns the reference to that value.
535    ///
536    /// Note that this method takes `&self`, not `&mut self`.
537    pub fn push(&self, val: T) -> &T {
538        self.push_back(val)
539    }
540
541    /// An almost same method with the [`std::iter::Extend::extend`],
542    /// though this method takes `&self` instead of `&mut self`.
543    ///
544    /// [`std::iter::Extend::extend`]: https://doc.rust-lang.org/std/iter/trait.Extend.html#tymethod.extend
545    pub fn extend<U: IntoIterator<Item = T>>(&self, iter: U) {
546        let alloc = self.allocator();
547
548        // Prefer the cached tail insertion slot when available, otherwise fall back to the head.
549        //
550        // IMPORTANT: Use `try_insert2` and retry on contention so that this method never drops
551        // values under `sync` (OnceLock) mode.
552        let mut next_cell = self.cache_mode.tail_slot_opt().unwrap_or(&self.head_slot);
553
554        for val in iter {
555            let mut new_cons = Box::new_in(Cons::new(val), A::clone(alloc));
556            loop {
557                match next_cell.try_insert2(new_cons) {
558                    Ok(inserted) => {
559                        self.cache_mode.on_push_success(&inserted.next);
560                        next_cell = &inserted.next;
561                        break;
562                    }
563                    Err((cur_cons, new_cons2)) => {
564                        next_cell = &cur_cons.next;
565                        new_cons = new_cons2;
566                    }
567                }
568            }
569        }
570    }
571}
572
573impl<T: ?Sized, A: Allocator + Default, C: Default> Default for OnceListCore<T, A, C> {
574    fn default() -> Self {
575        Self {
576            head_slot: NextSlot::new(),
577            alloc: A::default(),
578            cache_mode: C::default(),
579        }
580    }
581}
582
583impl<T: ?Sized + Debug, A: Allocator, C> Debug for OnceListCore<T, A, C>
584where
585    C: CacheMode<T, A>,
586{
587    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
588        f.debug_list().entries(self.iter()).finish()
589    }
590}
591
592impl<T: ?Sized + PartialEq, A: Allocator, C> PartialEq for OnceListCore<T, A, C>
593where
594    C: CacheMode<T, A>,
595{
596    fn eq(&self, other: &Self) -> bool {
597        self.iter().eq(other.iter())
598    }
599}
600
601impl<T: ?Sized + Eq, A: Allocator, C> Eq for OnceListCore<T, A, C> where C: CacheMode<T, A> {}
602
603impl<T: ?Sized + Hash, A: Allocator, C> Hash for OnceListCore<T, A, C>
604where
605    C: CacheMode<T, A>,
606{
607    fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
608        state.write_usize(self.len());
609        for val in self.iter() {
610            val.hash(state);
611        }
612    }
613}
614
615impl<T> FromIterator<T> for OnceListCore<T, Global, NoCache> {
616    fn from_iter<U: IntoIterator<Item = T>>(iter: U) -> Self {
617        let list = Self::new();
618        let mut next_cell = &list.head_slot;
619        for val in iter {
620            let new_cons = Box::new(Cons::new(val));
621            match next_cell.try_insert2(new_cons) {
622                Ok(inserted) => {
623                    next_cell = &inserted.next;
624                }
625                Err((_cur, _new_cons)) => {
626                    // This list is freshly created and not shared, so there should be no contention.
627                    unreachable!(
628                        "FromIterator: unexpected contention when inserting into a new list"
629                    );
630                }
631            }
632        }
633        list
634    }
635}
636
637impl<T, A: Allocator, C> IntoIterator for OnceListCore<T, A, C>
638where
639    C: CacheMode<T, A>,
640{
641    type Item = T;
642    type IntoIter = IntoIter<T, A>;
643
644    fn into_iter(self) -> Self::IntoIter {
645        IntoIter(self.head_slot)
646    }
647}
648
649impl<'a, T: ?Sized, A: Allocator, C> IntoIterator for &'a OnceListCore<T, A, C> {
650    type Item = &'a T;
651    type IntoIter = Iter<'a, T, A>;
652
653    fn into_iter(self) -> Self::IntoIter {
654        self.iter()
655    }
656}
657
658impl<'a, T: ?Sized, A: Allocator, C> IntoIterator for &'a mut OnceListCore<T, A, C> {
659    type Item = &'a mut T;
660    type IntoIter = IterMut<'a, T, A>;
661
662    fn into_iter(self) -> Self::IntoIter {
663        self.iter_mut()
664    }
665}
666
667impl<T, A: Allocator + Clone, C> Extend<T> for OnceListCore<T, A, C>
668where
669    C: CacheMode<T, A>,
670{
671    /// Due to the definition of the `Extend` trait, this method takes `&mut self`.
672    /// Use the [`OnceList::extend`] method instead if you want to use `&self`.
673    fn extend<U: IntoIterator<Item = T>>(&mut self, iter: U) {
674        // Call the inherent `extend(&self, ..)` method.
675        OnceListCore::<T, A, C>::extend(&*self, iter);
676    }
677}