std/sync/
once_lock.rs

1use super::once::OnceExclusiveState;
2use crate::cell::UnsafeCell;
3use crate::fmt;
4use crate::marker::PhantomData;
5use crate::mem::MaybeUninit;
6use crate::panic::{RefUnwindSafe, UnwindSafe};
7use crate::sync::Once;
8
9/// A synchronization primitive which can nominally be written to only once.
10///
11/// This type is a thread-safe [`OnceCell`], and can be used in statics.
12/// In many simple cases, you can use [`LazyLock<T, F>`] instead to get the benefits of this type
13/// with less effort: `LazyLock<T, F>` "looks like" `&T` because it initializes with `F` on deref!
14/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock
15/// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`].
16///
17/// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes
18/// initialized once written.
19///
20/// Unlike [`Mutex`](crate::sync::Mutex), `OnceLock` is never poisoned on panic.
21///
22/// [`OnceCell`]: crate::cell::OnceCell
23/// [`LazyLock<T, F>`]: crate::sync::LazyLock
24/// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new
25///
26/// # Examples
27///
28/// Writing to a `OnceLock` from a separate thread:
29///
30/// ```
31/// use std::sync::OnceLock;
32///
33/// static CELL: OnceLock<usize> = OnceLock::new();
34///
35/// // `OnceLock` has not been written to yet.
36/// assert!(CELL.get().is_none());
37///
38/// // Spawn a thread and write to `OnceLock`.
39/// std::thread::spawn(|| {
40///     let value = CELL.get_or_init(|| 12345);
41///     assert_eq!(value, &12345);
42/// })
43/// .join()
44/// .unwrap();
45///
46/// // `OnceLock` now contains the value.
47/// assert_eq!(
48///     CELL.get(),
49///     Some(&12345),
50/// );
51/// ```
52///
53/// You can use `OnceLock` to implement a type that requires "append-only" logic:
54///
55/// ```
56/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}};
57/// use std::thread;
58///
59/// struct OnceList<T> {
60///     data: OnceLock<T>,
61///     next: OnceLock<Box<OnceList<T>>>,
62/// }
63/// impl<T> OnceList<T> {
64///     const fn new() -> OnceList<T> {
65///         OnceList { data: OnceLock::new(), next: OnceLock::new() }
66///     }
67///     fn push(&self, value: T) {
68///         // FIXME: this impl is concise, but is also slow for long lists or many threads.
69///         // as an exercise, consider how you might improve on it while preserving the behavior
70///         if let Err(value) = self.data.set(value) {
71///             let next = self.next.get_or_init(|| Box::new(OnceList::new()));
72///             next.push(value)
73///         };
74///     }
75///     fn contains(&self, example: &T) -> bool
76///     where
77///         T: PartialEq,
78///     {
79///         self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| {
80///             self.next.get().map(|next| next.contains(example)).unwrap_or(false)
81///         })
82///     }
83/// }
84///
85/// // Let's exercise this new Sync append-only list by doing a little counting
86/// static LIST: OnceList<u32> = OnceList::new();
87/// static COUNTER: AtomicU32 = AtomicU32::new(0);
88///
89/// # const LEN: u32 = if cfg!(miri) { 50 } else { 1000 };
90/// # /*
91/// const LEN: u32 = 1000;
92/// # */
93/// thread::scope(|s| {
94///     for _ in 0..thread::available_parallelism().unwrap().get() {
95///         s.spawn(|| {
96///             while let i @ 0..LEN = COUNTER.fetch_add(1, Ordering::Relaxed) {
97///                 LIST.push(i);
98///             }
99///         });
100///     }
101/// });
102///
103/// for i in 0..LEN {
104///     assert!(LIST.contains(&i));
105/// }
106///
107/// ```
108#[stable(feature = "once_cell", since = "1.70.0")]
109pub struct OnceLock<T> {
110    // FIXME(nonpoison_once): switch to nonpoison version once it is available
111    once: Once,
112    // Whether or not the value is initialized is tracked by `once.is_completed()`.
113    value: UnsafeCell<MaybeUninit<T>>,
114    /// `PhantomData` to make sure dropck understands we're dropping T in our Drop impl.
115    ///
116    /// ```compile_fail,E0597
117    /// use std::sync::OnceLock;
118    ///
119    /// struct A<'a>(&'a str);
120    ///
121    /// impl<'a> Drop for A<'a> {
122    ///     fn drop(&mut self) {}
123    /// }
124    ///
125    /// let cell = OnceLock::new();
126    /// {
127    ///     let s = String::new();
128    ///     let _ = cell.set(A(&s));
129    /// }
130    /// ```
131    _marker: PhantomData<T>,
132}
133
134impl<T> OnceLock<T> {
135    /// Creates a new uninitialized cell.
136    #[inline]
137    #[must_use]
138    #[stable(feature = "once_cell", since = "1.70.0")]
139    #[rustc_const_stable(feature = "once_cell", since = "1.70.0")]
140    pub const fn new() -> OnceLock<T> {
141        OnceLock {
142            once: Once::new(),
143            value: UnsafeCell::new(MaybeUninit::uninit()),
144            _marker: PhantomData,
145        }
146    }
147
148    /// Gets the reference to the underlying value.
149    ///
150    /// Returns `None` if the cell is uninitialized, or being initialized.
151    /// This method never blocks.
152    #[inline]
153    #[stable(feature = "once_cell", since = "1.70.0")]
154    #[rustc_should_not_be_called_on_const_items]
155    pub fn get(&self) -> Option<&T> {
156        if self.initialized() {
157            // Safe b/c checked initialized
158            Some(unsafe { self.get_unchecked() })
159        } else {
160            None
161        }
162    }
163
164    /// Gets the mutable reference to the underlying value.
165    ///
166    /// Returns `None` if the cell is uninitialized.
167    ///
168    /// This method never blocks. Since it borrows the `OnceLock` mutably,
169    /// it is statically guaranteed that no active borrows to the `OnceLock`
170    /// exist, including from other threads.
171    #[inline]
172    #[stable(feature = "once_cell", since = "1.70.0")]
173    pub fn get_mut(&mut self) -> Option<&mut T> {
174        if self.initialized_mut() {
175            // Safe b/c checked initialized and we have a unique access
176            Some(unsafe { self.get_unchecked_mut() })
177        } else {
178            None
179        }
180    }
181
182    /// Blocks the current thread until the cell is initialized.
183    ///
184    /// # Example
185    ///
186    /// Waiting for a computation on another thread to finish:
187    /// ```rust
188    /// use std::thread;
189    /// use std::sync::OnceLock;
190    ///
191    /// let value = OnceLock::new();
192    ///
193    /// thread::scope(|s| {
194    ///     s.spawn(|| value.set(1 + 1));
195    ///
196    ///     let result = value.wait();
197    ///     assert_eq!(result, &2);
198    /// })
199    /// ```
200    #[inline]
201    #[stable(feature = "once_wait", since = "1.86.0")]
202    #[rustc_should_not_be_called_on_const_items]
203    pub fn wait(&self) -> &T {
204        self.once.wait_force();
205
206        unsafe { self.get_unchecked() }
207    }
208
209    /// Initializes the contents of the cell to `value`.
210    ///
211    /// May block if another thread is currently attempting to initialize the cell. The cell is
212    /// guaranteed to contain a value when `set` returns, though not necessarily the one provided.
213    ///
214    /// Returns `Ok(())` if the cell was uninitialized and
215    /// `Err(value)` if the cell was already initialized.
216    ///
217    /// # Examples
218    ///
219    /// ```
220    /// use std::sync::OnceLock;
221    ///
222    /// static CELL: OnceLock<i32> = OnceLock::new();
223    ///
224    /// fn main() {
225    ///     assert!(CELL.get().is_none());
226    ///
227    ///     std::thread::spawn(|| {
228    ///         assert_eq!(CELL.set(92), Ok(()));
229    ///     }).join().unwrap();
230    ///
231    ///     assert_eq!(CELL.set(62), Err(62));
232    ///     assert_eq!(CELL.get(), Some(&92));
233    /// }
234    /// ```
235    #[inline]
236    #[stable(feature = "once_cell", since = "1.70.0")]
237    #[rustc_should_not_be_called_on_const_items]
238    pub fn set(&self, value: T) -> Result<(), T> {
239        match self.try_insert(value) {
240            Ok(_) => Ok(()),
241            Err((_, value)) => Err(value),
242        }
243    }
244
245    /// Initializes the contents of the cell to `value` if the cell was uninitialized,
246    /// then returns a reference to it.
247    ///
248    /// May block if another thread is currently attempting to initialize the cell. The cell is
249    /// guaranteed to contain a value when `try_insert` returns, though not necessarily the
250    /// one provided.
251    ///
252    /// Returns `Ok(&value)` if the cell was uninitialized and
253    /// `Err((&current_value, value))` if it was already initialized.
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// #![feature(once_cell_try_insert)]
259    ///
260    /// use std::sync::OnceLock;
261    ///
262    /// static CELL: OnceLock<i32> = OnceLock::new();
263    ///
264    /// fn main() {
265    ///     assert!(CELL.get().is_none());
266    ///
267    ///     std::thread::spawn(|| {
268    ///         assert_eq!(CELL.try_insert(92), Ok(&92));
269    ///     }).join().unwrap();
270    ///
271    ///     assert_eq!(CELL.try_insert(62), Err((&92, 62)));
272    ///     assert_eq!(CELL.get(), Some(&92));
273    /// }
274    /// ```
275    #[inline]
276    #[unstable(feature = "once_cell_try_insert", issue = "116693")]
277    #[rustc_should_not_be_called_on_const_items]
278    pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> {
279        let mut value = Some(value);
280        let res = self.get_or_init(|| value.take().unwrap());
281        match value {
282            None => Ok(res),
283            Some(value) => Err((res, value)),
284        }
285    }
286
287    /// Gets the contents of the cell, initializing it to `f()` if the cell
288    /// was uninitialized.
289    ///
290    /// Many threads may call `get_or_init` concurrently with different
291    /// initializing functions, but it is guaranteed that only one function
292    /// will be executed if the function doesn't panic.
293    ///
294    /// # Panics
295    ///
296    /// If `f()` panics, the panic is propagated to the caller, and the cell
297    /// remains uninitialized.
298    ///
299    /// It is an error to reentrantly initialize the cell from `f`. The
300    /// exact outcome is unspecified. Current implementation deadlocks, but
301    /// this may be changed to a panic in the future.
302    ///
303    /// # Examples
304    ///
305    /// ```
306    /// use std::sync::OnceLock;
307    ///
308    /// let cell = OnceLock::new();
309    /// let value = cell.get_or_init(|| 92);
310    /// assert_eq!(value, &92);
311    /// let value = cell.get_or_init(|| unreachable!());
312    /// assert_eq!(value, &92);
313    /// ```
314    #[inline]
315    #[stable(feature = "once_cell", since = "1.70.0")]
316    #[rustc_should_not_be_called_on_const_items]
317    pub fn get_or_init<F>(&self, f: F) -> &T
318    where
319        F: FnOnce() -> T,
320    {
321        match self.get_or_try_init(|| Ok::<T, !>(f())) {
322            Ok(val) => val,
323        }
324    }
325
326    /// Gets the mutable reference of the contents of the cell, initializing
327    /// it to `f()` if the cell was uninitialized.
328    ///
329    /// This method never blocks. Since it borrows the `OnceLock` mutably,
330    /// it is statically guaranteed that no active borrows to the `OnceLock`
331    /// exist, including from other threads.
332    ///
333    /// # Panics
334    ///
335    /// If `f()` panics, the panic is propagated to the caller, and the cell
336    /// remains uninitialized.
337    ///
338    /// # Examples
339    ///
340    /// ```
341    /// #![feature(once_cell_get_mut)]
342    ///
343    /// use std::sync::OnceLock;
344    ///
345    /// let mut cell = OnceLock::new();
346    /// let value = cell.get_mut_or_init(|| 92);
347    /// assert_eq!(*value, 92);
348    ///
349    /// *value += 2;
350    /// assert_eq!(*value, 94);
351    ///
352    /// let value = cell.get_mut_or_init(|| unreachable!());
353    /// assert_eq!(*value, 94);
354    /// ```
355    #[inline]
356    #[unstable(feature = "once_cell_get_mut", issue = "121641")]
357    pub fn get_mut_or_init<F>(&mut self, f: F) -> &mut T
358    where
359        F: FnOnce() -> T,
360    {
361        match self.get_mut_or_try_init(|| Ok::<T, !>(f())) {
362            Ok(val) => val,
363        }
364    }
365
366    /// Gets the contents of the cell, initializing it to `f()` if
367    /// the cell was uninitialized. If the cell was uninitialized
368    /// and `f()` failed, an error is returned.
369    ///
370    /// # Panics
371    ///
372    /// If `f()` panics, the panic is propagated to the caller, and
373    /// the cell remains uninitialized.
374    ///
375    /// It is an error to reentrantly initialize the cell from `f`.
376    /// The exact outcome is unspecified. Current implementation
377    /// deadlocks, but this may be changed to a panic in the future.
378    ///
379    /// # Examples
380    ///
381    /// ```
382    /// #![feature(once_cell_try)]
383    ///
384    /// use std::sync::OnceLock;
385    ///
386    /// let cell = OnceLock::new();
387    /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
388    /// assert!(cell.get().is_none());
389    /// let value = cell.get_or_try_init(|| -> Result<i32, ()> {
390    ///     Ok(92)
391    /// });
392    /// assert_eq!(value, Ok(&92));
393    /// assert_eq!(cell.get(), Some(&92))
394    /// ```
395    #[inline]
396    #[unstable(feature = "once_cell_try", issue = "109737")]
397    #[rustc_should_not_be_called_on_const_items]
398    pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
399    where
400        F: FnOnce() -> Result<T, E>,
401    {
402        // Fast path check
403        // NOTE: We need to perform an acquire on the state in this method
404        // in order to correctly synchronize `LazyLock::force`. This is
405        // currently done by calling `self.get()`, which in turn calls
406        // `self.initialized()`, which in turn performs the acquire.
407        if let Some(value) = self.get() {
408            return Ok(value);
409        }
410        self.initialize(f)?;
411
412        // SAFETY: The inner value has been initialized
413        Ok(unsafe { self.get_unchecked() })
414    }
415
416    /// Gets the mutable reference of the contents of the cell, initializing
417    /// it to `f()` if the cell was uninitialized. If the cell was uninitialized
418    /// and `f()` failed, an error is returned.
419    ///
420    /// This method never blocks. Since it borrows the `OnceLock` mutably,
421    /// it is statically guaranteed that no active borrows to the `OnceLock`
422    /// exist, including from other threads.
423    ///
424    /// # Panics
425    ///
426    /// If `f()` panics, the panic is propagated to the caller, and
427    /// the cell remains uninitialized.
428    ///
429    /// # Examples
430    ///
431    /// ```
432    /// #![feature(once_cell_get_mut)]
433    ///
434    /// use std::sync::OnceLock;
435    ///
436    /// let mut cell: OnceLock<u32> = OnceLock::new();
437    ///
438    /// // Failed attempts to initialize the cell do not change its contents
439    /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err());
440    /// assert!(cell.get().is_none());
441    ///
442    /// let value = cell.get_mut_or_try_init(|| "1234".parse());
443    /// assert_eq!(value, Ok(&mut 1234));
444    /// *value.unwrap() += 2;
445    /// assert_eq!(cell.get(), Some(&1236))
446    /// ```
447    #[inline]
448    #[unstable(feature = "once_cell_get_mut", issue = "121641")]
449    pub fn get_mut_or_try_init<F, E>(&mut self, f: F) -> Result<&mut T, E>
450    where
451        F: FnOnce() -> Result<T, E>,
452    {
453        if self.get_mut().is_none() {
454            self.initialize(f)?;
455        }
456
457        // SAFETY: The inner value has been initialized
458        Ok(unsafe { self.get_unchecked_mut() })
459    }
460
461    /// Consumes the `OnceLock`, returning the wrapped value. Returns
462    /// `None` if the cell was uninitialized.
463    ///
464    /// # Examples
465    ///
466    /// ```
467    /// use std::sync::OnceLock;
468    ///
469    /// let cell: OnceLock<String> = OnceLock::new();
470    /// assert_eq!(cell.into_inner(), None);
471    ///
472    /// let cell = OnceLock::new();
473    /// cell.set("hello".to_string()).unwrap();
474    /// assert_eq!(cell.into_inner(), Some("hello".to_string()));
475    /// ```
476    #[inline]
477    #[stable(feature = "once_cell", since = "1.70.0")]
478    pub fn into_inner(mut self) -> Option<T> {
479        self.take()
480    }
481
482    /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state.
483    ///
484    /// Has no effect and returns `None` if the `OnceLock` was uninitialized.
485    ///
486    /// Since this method borrows the `OnceLock` mutably, it is statically guaranteed that
487    /// no active borrows to the `OnceLock` exist, including from other threads.
488    ///
489    /// # Examples
490    ///
491    /// ```
492    /// use std::sync::OnceLock;
493    ///
494    /// let mut cell: OnceLock<String> = OnceLock::new();
495    /// assert_eq!(cell.take(), None);
496    ///
497    /// let mut cell = OnceLock::new();
498    /// cell.set("hello".to_string()).unwrap();
499    /// assert_eq!(cell.take(), Some("hello".to_string()));
500    /// assert_eq!(cell.get(), None);
501    /// ```
502    #[inline]
503    #[stable(feature = "once_cell", since = "1.70.0")]
504    pub fn take(&mut self) -> Option<T> {
505        if self.initialized_mut() {
506            self.once = Once::new();
507            // SAFETY: `self.value` is initialized and contains a valid `T`.
508            // `self.once` is reset, so `initialized()` will be false again
509            // which prevents the value from being read twice.
510            unsafe { Some(self.value.get_mut().assume_init_read()) }
511        } else {
512            None
513        }
514    }
515
516    #[inline]
517    fn initialized(&self) -> bool {
518        self.once.is_completed()
519    }
520
521    #[inline]
522    fn initialized_mut(&mut self) -> bool {
523        // `state()` does not perform an atomic load, so prefer it over `is_complete()`.
524        let state = self.once.state();
525        match state {
526            OnceExclusiveState::Complete => true,
527            _ => false,
528        }
529    }
530
531    #[cold]
532    #[optimize(size)]
533    fn initialize<F, E>(&self, f: F) -> Result<(), E>
534    where
535        F: FnOnce() -> Result<T, E>,
536    {
537        let mut res: Result<(), E> = Ok(());
538        let slot = &self.value;
539
540        // Ignore poisoning from other threads
541        // If another thread panics, then we'll be able to run our closure
542        self.once.call_once_force(|p| {
543            match f() {
544                Ok(value) => {
545                    unsafe { (&mut *slot.get()).write(value) };
546                }
547                Err(e) => {
548                    res = Err(e);
549
550                    // Treat the underlying `Once` as poisoned since we
551                    // failed to initialize our value.
552                    p.poison();
553                }
554            }
555        });
556        res
557    }
558
559    /// # Safety
560    ///
561    /// The cell must be initialized
562    #[inline]
563    unsafe fn get_unchecked(&self) -> &T {
564        debug_assert!(self.initialized());
565        unsafe { (&*self.value.get()).assume_init_ref() }
566    }
567
568    /// # Safety
569    ///
570    /// The cell must be initialized
571    #[inline]
572    unsafe fn get_unchecked_mut(&mut self) -> &mut T {
573        debug_assert!(self.initialized_mut());
574        unsafe { self.value.get_mut().assume_init_mut() }
575    }
576}
577
578// Why do we need `T: Send`?
579// Thread A creates a `OnceLock` and shares it with
580// scoped thread B, which fills the cell, which is
581// then destroyed by A. That is, destructor observes
582// a sent value.
583#[stable(feature = "once_cell", since = "1.70.0")]
584unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
585#[stable(feature = "once_cell", since = "1.70.0")]
586unsafe impl<T: Send> Send for OnceLock<T> {}
587
588#[stable(feature = "once_cell", since = "1.70.0")]
589impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceLock<T> {}
590#[stable(feature = "once_cell", since = "1.70.0")]
591impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {}
592
593#[stable(feature = "once_cell", since = "1.70.0")]
594#[rustc_const_unstable(feature = "const_default", issue = "143894")]
595impl<T> const Default for OnceLock<T> {
596    /// Creates a new uninitialized cell.
597    ///
598    /// # Example
599    ///
600    /// ```
601    /// use std::sync::OnceLock;
602    ///
603    /// fn main() {
604    ///     assert_eq!(OnceLock::<()>::new(), OnceLock::default());
605    /// }
606    /// ```
607    #[inline]
608    fn default() -> OnceLock<T> {
609        OnceLock::new()
610    }
611}
612
613#[stable(feature = "once_cell", since = "1.70.0")]
614impl<T: fmt::Debug> fmt::Debug for OnceLock<T> {
615    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
616        let mut d = f.debug_tuple("OnceLock");
617        match self.get() {
618            Some(v) => d.field(v),
619            None => d.field(&format_args!("<uninit>")),
620        };
621        d.finish()
622    }
623}
624
625#[stable(feature = "once_cell", since = "1.70.0")]
626impl<T: Clone> Clone for OnceLock<T> {
627    #[inline]
628    fn clone(&self) -> OnceLock<T> {
629        let cell = Self::new();
630        if let Some(value) = self.get() {
631            match cell.set(value.clone()) {
632                Ok(()) => (),
633                Err(_) => unreachable!(),
634            }
635        }
636        cell
637    }
638}
639
640#[stable(feature = "once_cell", since = "1.70.0")]
641impl<T> From<T> for OnceLock<T> {
642    /// Creates a new cell with its contents set to `value`.
643    ///
644    /// # Example
645    ///
646    /// ```
647    /// use std::sync::OnceLock;
648    ///
649    /// # fn main() -> Result<(), i32> {
650    /// let a = OnceLock::from(3);
651    /// let b = OnceLock::new();
652    /// b.set(3)?;
653    /// assert_eq!(a, b);
654    /// Ok(())
655    /// # }
656    /// ```
657    #[inline]
658    fn from(value: T) -> Self {
659        let cell = Self::new();
660        match cell.set(value) {
661            Ok(()) => cell,
662            Err(_) => unreachable!(),
663        }
664    }
665}
666
667#[stable(feature = "once_cell", since = "1.70.0")]
668impl<T: PartialEq> PartialEq for OnceLock<T> {
669    /// Equality for two `OnceLock`s.
670    ///
671    /// Two `OnceLock`s are equal if they either both contain values and their
672    /// values are equal, or if neither contains a value.
673    ///
674    /// # Examples
675    ///
676    /// ```
677    /// use std::sync::OnceLock;
678    ///
679    /// let five = OnceLock::new();
680    /// five.set(5).unwrap();
681    ///
682    /// let also_five = OnceLock::new();
683    /// also_five.set(5).unwrap();
684    ///
685    /// assert!(five == also_five);
686    ///
687    /// assert!(OnceLock::<u32>::new() == OnceLock::<u32>::new());
688    /// ```
689    #[inline]
690    fn eq(&self, other: &OnceLock<T>) -> bool {
691        self.get() == other.get()
692    }
693}
694
695#[stable(feature = "once_cell", since = "1.70.0")]
696impl<T: Eq> Eq for OnceLock<T> {}
697
698#[stable(feature = "once_cell", since = "1.70.0")]
699unsafe impl<#[may_dangle] T> Drop for OnceLock<T> {
700    #[inline]
701    fn drop(&mut self) {
702        if self.initialized_mut() {
703            // SAFETY: The cell is initialized and being dropped, so it can't
704            // be accessed again. We also don't touch the `T` other than
705            // dropping it, which validates our usage of #[may_dangle].
706            unsafe { self.value.get_mut().assume_init_drop() };
707        }
708    }
709}