dioxus_signals/
write.rs

1use std::{
2    collections::{HashMap, HashSet},
3    ops::{Deref, DerefMut, IndexMut},
4};
5
6use generational_box::{AnyStorage, UnsyncStorage};
7
8use crate::{ext_methods, read::Readable, read::ReadableExt, MappedMutSignal, WriteSignal};
9
10/// A reference to a value that can be written to.
11#[allow(type_alias_bounds)]
12pub type WritableRef<'a, T: Writable, O = <T as Readable>::Target> =
13    WriteLock<'a, O, <T as Readable>::Storage, <T as Writable>::WriteMetadata>;
14
15/// A trait for states that can be written to like [`crate::Signal`]. You may choose to accept this trait as a parameter instead of the concrete type to allow for more flexibility in your API.
16///
17/// # Example
18/// ```rust
19/// # use dioxus::prelude::*;
20/// enum MyEnum {
21///     String(String),
22///     Number(i32),
23/// }
24///
25/// fn MyComponent(mut count: Signal<MyEnum>) -> Element {
26///     rsx! {
27///         button {
28///             onclick: move |_| {
29///                 // You can use any methods from the Writable trait on Signals
30///                 match &mut *count.write() {
31///                     MyEnum::String(s) => s.push('a'),
32///                     MyEnum::Number(n) => *n += 1,
33///                 }
34///             },
35///             "Add value"
36///         }
37///     }
38/// }
39/// ```
40pub trait Writable: Readable {
41    /// Additional data associated with the write reference.
42    type WriteMetadata;
43
44    /// Try to get a mutable reference to the value without checking the lifetime. This will update any subscribers.
45    ///
46    /// NOTE: This method is completely safe because borrow checking is done at runtime.
47    fn try_write_unchecked(
48        &self,
49    ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError>
50    where
51        Self::Target: 'static;
52}
53
54/// A mutable reference to a writable value. This reference acts similarly to [`std::cell::RefMut`], but it has extra debug information
55/// and integrates with the reactive system to automatically update dependents.
56///
57/// [`WriteLock`] implements [`DerefMut`] which means you can call methods on the inner value just like you would on a mutable reference
58/// to the inner value. If you need to get the inner reference directly, you can call [`WriteLock::deref_mut`].
59///
60/// # Example
61/// ```rust
62/// # use dioxus::prelude::*;
63/// fn app() -> Element {
64///     let mut value = use_signal(|| String::from("hello"));
65///
66///     rsx! {
67///         button {
68///             onclick: move |_| {
69///                 let mut mutable_reference = value.write();
70///
71///                 // You call methods like `push_str` on the reference just like you would with the inner String
72///                 mutable_reference.push_str("world");
73///             },
74///             "Click to add world to the string"
75///         }
76///         div { "{value}" }
77///     }
78/// }
79/// ```
80///
81/// ## Matching on WriteLock
82///
83/// You need to get the inner mutable reference with [`WriteLock::deref_mut`] before you match the inner value. If you try to match
84/// without calling [`WriteLock::deref_mut`], you will get an error like this:
85///
86/// ```compile_fail
87/// # use dioxus::prelude::*;
88/// #[derive(Debug)]
89/// enum Colors {
90///     Red(u32),
91///     Green
92/// }
93/// fn app() -> Element {
94///     let mut value = use_signal(|| Colors::Red(0));
95///
96///     rsx! {
97///         button {
98///             onclick: move |_| {
99///                 let mut mutable_reference = value.write();
100///
101///                 match mutable_reference {
102///                     // Since we are matching on the `Write` type instead of &mut Colors, we can't match on the enum directly
103///                     Colors::Red(brightness) => *brightness += 1,
104///                     Colors::Green => {}
105///                 }
106///             },
107///             "Click to add brightness to the red color"
108///         }
109///         div { "{value:?}" }
110///     }
111/// }
112/// ```
113///
114/// ```text
115/// error[E0308]: mismatched types
116///   --> src/main.rs:18:21
117///    |
118/// 16 |                 match mutable_reference {
119///    |                       ----------------- this expression has type `dioxus::prelude::Write<'_, Colors>`
120/// 17 |                     // Since we are matching on the `Write` t...
121/// 18 |                     Colors::Red(brightness) => *brightness += 1,
122///    |                     ^^^^^^^^^^^^^^^^^^^^^^^ expected `Write<'_, Colors>`, found `Colors`
123///    |
124///    = note: expected struct `dioxus::prelude::Write<'_, Colors, >`
125///                found enum `Colors`
126/// ```
127///
128/// Instead, you need to call deref mut on the reference to get the inner value **before** you match on it:
129///
130/// ```rust
131/// use std::ops::DerefMut;
132/// # use dioxus::prelude::*;
133/// #[derive(Debug)]
134/// enum Colors {
135///     Red(u32),
136///     Green
137/// }
138/// fn app() -> Element {
139///     let mut value = use_signal(|| Colors::Red(0));
140///
141///     rsx! {
142///         button {
143///             onclick: move |_| {
144///                 let mut mutable_reference = value.write();
145///
146///                 // DerefMut converts the `Write` into a `&mut Colors`
147///                 match mutable_reference.deref_mut() {
148///                     // Now we can match on the inner value
149///                     Colors::Red(brightness) => *brightness += 1,
150///                     Colors::Green => {}
151///                 }
152///             },
153///             "Click to add brightness to the red color"
154///         }
155///         div { "{value:?}" }
156///     }
157/// }
158/// ```
159///
160/// ## Generics
161/// - T is the current type of the write
162/// - S is the storage type of the signal. This type determines if the signal is local to the current thread, or it can be shared across threads.
163/// - D is the additional data associated with the write reference. This is used by signals to track when the write is dropped
164pub struct WriteLock<'a, T: ?Sized + 'a, S: AnyStorage = UnsyncStorage, D = ()> {
165    write: S::Mut<'a, T>,
166    data: D,
167}
168
169impl<'a, T: ?Sized, S: AnyStorage> WriteLock<'a, T, S> {
170    /// Create a new write reference
171    pub fn new(write: S::Mut<'a, T>) -> Self {
172        Self { write, data: () }
173    }
174}
175
176impl<'a, T: ?Sized, S: AnyStorage, D> WriteLock<'a, T, S, D> {
177    /// Create a new write reference with additional data.
178    pub fn new_with_metadata(write: S::Mut<'a, T>, data: D) -> Self {
179        Self { write, data }
180    }
181
182    /// Get the inner value of the write reference.
183    pub fn into_inner(self) -> S::Mut<'a, T> {
184        self.write
185    }
186
187    /// Get the additional data associated with the write reference.
188    pub fn data(&self) -> &D {
189        &self.data
190    }
191
192    /// Split into the inner value and the additional data.
193    pub fn into_parts(self) -> (S::Mut<'a, T>, D) {
194        (self.write, self.data)
195    }
196
197    /// Map the metadata of the write reference to a new type.
198    pub fn map_metadata<O>(self, f: impl FnOnce(D) -> O) -> WriteLock<'a, T, S, O> {
199        WriteLock {
200            write: self.write,
201            data: f(self.data),
202        }
203    }
204
205    /// Map the mutable reference to the signal's value to a new type.
206    pub fn map<O: ?Sized>(
207        myself: Self,
208        f: impl FnOnce(&mut T) -> &mut O,
209    ) -> WriteLock<'a, O, S, D> {
210        let Self { write, data, .. } = myself;
211        WriteLock {
212            write: S::map_mut(write, f),
213            data,
214        }
215    }
216
217    /// Try to map the mutable reference to the signal's value to a new type
218    pub fn filter_map<O: ?Sized>(
219        myself: Self,
220        f: impl FnOnce(&mut T) -> Option<&mut O>,
221    ) -> Option<WriteLock<'a, O, S, D>> {
222        let Self { write, data, .. } = myself;
223        let write = S::try_map_mut(write, f);
224        write.map(|write| WriteLock { write, data })
225    }
226
227    /// Downcast the lifetime of the mutable reference to the signal's value.
228    ///
229    /// This function enforces the variance of the lifetime parameter `'a` in Mut.  Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
230    pub fn downcast_lifetime<'b>(mut_: Self) -> WriteLock<'b, T, S, D>
231    where
232        'a: 'b,
233    {
234        WriteLock {
235            write: S::downcast_lifetime_mut(mut_.write),
236            data: mut_.data,
237        }
238    }
239}
240
241impl<T, S, D> Deref for WriteLock<'_, T, S, D>
242where
243    S: AnyStorage,
244    T: ?Sized,
245{
246    type Target = T;
247
248    fn deref(&self) -> &Self::Target {
249        &self.write
250    }
251}
252
253impl<T, S, D> DerefMut for WriteLock<'_, T, S, D>
254where
255    S: AnyStorage,
256    T: ?Sized,
257{
258    fn deref_mut(&mut self) -> &mut Self::Target {
259        &mut self.write
260    }
261}
262
263/// An extension trait for [`Writable`] that provides some convenience methods.
264pub trait WritableExt: Writable {
265    /// Get a mutable reference to the value. If the value has been dropped, this will panic.
266    #[track_caller]
267    fn write(&mut self) -> WritableRef<'_, Self>
268    where
269        Self::Target: 'static,
270    {
271        self.try_write().unwrap()
272    }
273
274    /// Try to get a mutable reference to the value.
275    #[track_caller]
276    fn try_write(&mut self) -> Result<WritableRef<'_, Self>, generational_box::BorrowMutError>
277    where
278        Self::Target: 'static,
279    {
280        self.try_write_unchecked().map(WriteLock::downcast_lifetime)
281    }
282
283    /// Get a mutable reference to the value without checking the lifetime. This will update any subscribers.
284    ///
285    /// NOTE: This method is completely safe because borrow checking is done at runtime.
286    #[track_caller]
287    fn write_unchecked(&self) -> WritableRef<'static, Self>
288    where
289        Self::Target: 'static,
290    {
291        self.try_write_unchecked().unwrap()
292    }
293
294    /// Map the references and mutable references of the writable value to a new type. This lets you provide a view
295    /// into the writable value without creating a new signal or cloning the value.
296    ///
297    /// Anything that subscribes to the writable value will be rerun whenever the original value changes or you write to this
298    /// scoped value, even if the view does not change. If you want to memorize the view, you can use a [`crate::Memo`] instead.
299    /// For fine grained scoped updates, use stores instead
300    ///
301    /// # Example
302    /// ```rust
303    /// # use dioxus::prelude::*;
304    /// fn List(list: Signal<Vec<i32>>) -> Element {
305    ///     rsx! {
306    ///         for index in 0..list.len() {
307    ///             // We can use the `map` method to provide a view into the single item in the list that the child component will render
308    ///             Item { item: list.map_mut(move |v| &v[index], move |v| &mut v[index]) }
309    ///         }
310    ///     }
311    /// }
312    ///
313    /// // The child component doesn't need to know that the mapped value is coming from a list
314    /// #[component]
315    /// fn Item(item: WriteSignal<i32>) -> Element {
316    ///     rsx! {
317    ///         button {
318    ///             onclick: move |_| *item.write() += 1,
319    ///             "{item}"
320    ///         }
321    ///     }
322    /// }
323    /// ```
324    fn map_mut<O, F, FMut>(self, f: F, f_mut: FMut) -> MappedMutSignal<O, Self, F, FMut>
325    where
326        Self: Sized,
327        O: ?Sized,
328        F: Fn(&Self::Target) -> &O,
329        FMut: Fn(&mut Self::Target) -> &mut O,
330    {
331        MappedMutSignal::new(self, f, f_mut)
332    }
333
334    /// Run a function with a mutable reference to the value. If the value has been dropped, this will panic.
335    #[track_caller]
336    fn with_mut<O>(&mut self, f: impl FnOnce(&mut Self::Target) -> O) -> O
337    where
338        Self::Target: 'static,
339    {
340        f(&mut *self.write())
341    }
342
343    /// Set the value of the signal. This will trigger an update on all subscribers.
344    #[track_caller]
345    fn set(&mut self, value: Self::Target)
346    where
347        Self::Target: Sized + 'static,
348    {
349        *self.write() = value;
350    }
351
352    /// Invert the boolean value of the signal. This will trigger an update on all subscribers.
353    #[track_caller]
354    fn toggle(&mut self)
355    where
356        Self::Target: std::ops::Not<Output = Self::Target> + Clone + 'static,
357    {
358        let inverted = !(*self.peek()).clone();
359        self.set(inverted);
360    }
361
362    /// Index into the inner value and return a reference to the result.
363    #[track_caller]
364    fn index_mut<I>(
365        &mut self,
366        index: I,
367    ) -> WritableRef<'_, Self, <Self::Target as std::ops::Index<I>>::Output>
368    where
369        Self::Target: std::ops::IndexMut<I> + 'static,
370    {
371        WriteLock::map(self.write(), |v| v.index_mut(index))
372    }
373
374    /// Takes the value out of the Signal, leaving a Default in its place.
375    #[track_caller]
376    fn take(&mut self) -> Self::Target
377    where
378        Self::Target: Default + 'static,
379    {
380        self.with_mut(std::mem::take)
381    }
382
383    /// Replace the value in the Signal, returning the old value.
384    #[track_caller]
385    fn replace(&mut self, value: Self::Target) -> Self::Target
386    where
387        Self::Target: Sized + 'static,
388    {
389        self.with_mut(|v| std::mem::replace(v, value))
390    }
391}
392
393impl<W: Writable + ?Sized> WritableExt for W {}
394
395/// An extension trait for [`Writable`] values that can be boxed into a trait object.
396pub trait WritableBoxedExt: Writable<Storage = UnsyncStorage> {
397    /// Box the writable value into a trait object. This is useful for passing around writable values without knowing their concrete type.
398    fn boxed_mut(self) -> WriteSignal<Self::Target>
399    where
400        Self: Sized + 'static,
401    {
402        WriteSignal::new(self)
403    }
404}
405
406impl<T: Writable<Storage = UnsyncStorage> + 'static> WritableBoxedExt for T {
407    fn boxed_mut(self) -> WriteSignal<Self::Target> {
408        WriteSignal::new(self)
409    }
410}
411
412/// An extension trait for [`Writable<Option<T>>`]` that provides some convenience methods.
413pub trait WritableOptionExt<T>: Writable<Target = Option<T>> {
414    /// Gets the value out of the Option, or inserts the given value if the Option is empty.
415    #[track_caller]
416    fn get_or_insert(&mut self, default: T) -> WritableRef<'_, Self, T>
417    where
418        T: 'static,
419    {
420        self.get_or_insert_with(|| default)
421    }
422
423    /// Gets the value out of the Option, or inserts the value returned by the given function if the Option is empty.
424    #[track_caller]
425    fn get_or_insert_with(&mut self, default: impl FnOnce() -> T) -> WritableRef<'_, Self, T>
426    where
427        T: 'static,
428    {
429        let is_none = self.read().is_none();
430        if is_none {
431            self.with_mut(|v| *v = Some(default()));
432            WriteLock::map(self.write(), |v| v.as_mut().unwrap())
433        } else {
434            WriteLock::map(self.write(), |v| v.as_mut().unwrap())
435        }
436    }
437
438    /// Attempts to write the inner value of the Option.
439    #[track_caller]
440    fn as_mut(&mut self) -> Option<WritableRef<'_, Self, T>>
441    where
442        T: 'static,
443    {
444        WriteLock::filter_map(self.write(), |v: &mut Option<T>| v.as_mut())
445    }
446}
447
448impl<T, W> WritableOptionExt<T> for W where W: Writable<Target = Option<T>> {}
449
450/// An extension trait for [`Writable<Vec<T>>`] that provides some convenience methods.
451pub trait WritableVecExt<T>: Writable<Target = Vec<T>> {
452    /// Pushes a new value to the end of the vector.
453    #[track_caller]
454    fn push(&mut self, value: T)
455    where
456        T: 'static,
457    {
458        self.with_mut(|v| v.push(value))
459    }
460
461    /// Pops the last value from the vector.
462    #[track_caller]
463    fn pop(&mut self) -> Option<T>
464    where
465        T: 'static,
466    {
467        self.with_mut(|v| v.pop())
468    }
469
470    /// Inserts a new value at the given index.
471    #[track_caller]
472    fn insert(&mut self, index: usize, value: T)
473    where
474        T: 'static,
475    {
476        self.with_mut(|v| v.insert(index, value))
477    }
478
479    /// Removes the value at the given index.
480    #[track_caller]
481    fn remove(&mut self, index: usize) -> T
482    where
483        T: 'static,
484    {
485        self.with_mut(|v| v.remove(index))
486    }
487
488    /// Clears the vector, removing all values.
489    #[track_caller]
490    fn clear(&mut self)
491    where
492        T: 'static,
493    {
494        self.with_mut(|v| v.clear())
495    }
496
497    /// Extends the vector with the given iterator.
498    #[track_caller]
499    fn extend(&mut self, iter: impl IntoIterator<Item = T>)
500    where
501        T: 'static,
502    {
503        self.with_mut(|v| v.extend(iter))
504    }
505
506    /// Truncates the vector to the given length.
507    #[track_caller]
508    fn truncate(&mut self, len: usize)
509    where
510        T: 'static,
511    {
512        self.with_mut(|v| v.truncate(len))
513    }
514
515    /// Swaps two values in the vector.
516    #[track_caller]
517    fn swap_remove(&mut self, index: usize) -> T
518    where
519        T: 'static,
520    {
521        self.with_mut(|v| v.swap_remove(index))
522    }
523
524    /// Retains only the values that match the given predicate.
525    #[track_caller]
526    fn retain(&mut self, f: impl FnMut(&T) -> bool)
527    where
528        T: 'static,
529    {
530        self.with_mut(|v| v.retain(f))
531    }
532
533    /// Splits the vector into two at the given index.
534    #[track_caller]
535    fn split_off(&mut self, at: usize) -> Vec<T>
536    where
537        T: 'static,
538    {
539        self.with_mut(|v| v.split_off(at))
540    }
541
542    /// Try to mutably get an element from the vector.
543    #[track_caller]
544    fn get_mut(&mut self, index: usize) -> Option<WritableRef<'_, Self, T>>
545    where
546        T: 'static,
547    {
548        WriteLock::filter_map(self.write(), |v: &mut Vec<T>| v.get_mut(index))
549    }
550
551    /// Gets an iterator over the values of the vector.
552    #[track_caller]
553    fn iter_mut(&mut self) -> WritableValueIterator<'_, Self>
554    where
555        Self: Sized + Clone,
556    {
557        WritableValueIterator {
558            index: 0,
559            value: self,
560        }
561    }
562}
563
564/// An iterator over the values of a [`Writable<Vec<T>>`].
565pub struct WritableValueIterator<'a, R> {
566    index: usize,
567    value: &'a mut R,
568}
569
570impl<'a, T: 'static, R: Writable<Target = Vec<T>>> Iterator for WritableValueIterator<'a, R> {
571    type Item = WritableRef<'a, R, T>;
572
573    fn next(&mut self) -> Option<Self::Item> {
574        let index = self.index;
575        self.index += 1;
576        WriteLock::filter_map(
577            self.value.try_write_unchecked().unwrap(),
578            |v: &mut Vec<T>| v.get_mut(index),
579        )
580        .map(WriteLock::downcast_lifetime)
581    }
582}
583
584impl<W, T> WritableVecExt<T> for W where W: Writable<Target = Vec<T>> {}
585
586/// An extension trait for [`Writable<String>`] that provides some convenience methods.
587pub trait WritableStringExt: Writable<Target = String> {
588    ext_methods! {
589        /// Pushes a character to the end of the string.
590        fn push_str(&mut self, s: &str) = String::push_str;
591
592        /// Pushes a character to the end of the string.
593        fn push(&mut self, c: char) = String::push;
594
595        /// Pops a character from the end of the string.
596        fn pop(&mut self) -> Option<char> = String::pop;
597
598        /// Inserts a string at the given index.
599        fn insert_str(&mut self, idx: usize, s: &str) = String::insert_str;
600
601        /// Inserts a character at the given index.
602        fn insert(&mut self, idx: usize, c: char) = String::insert;
603
604        /// Remove a character at the given index
605        fn remove(&mut self, idx: usize) -> char = String::remove;
606
607        /// Replace a range of the string with the given string.
608        fn replace_range(&mut self, range: impl std::ops::RangeBounds<usize>, replace_with: &str) = String::replace_range;
609
610        /// Clears the string, removing all characters.
611        fn clear(&mut self) = String::clear;
612
613        /// Extends the string with the given iterator of characters.
614        fn extend(&mut self, iter: impl IntoIterator<Item = char>) = String::extend;
615
616        /// Truncates the string to the given length.
617        fn truncate(&mut self, len: usize) = String::truncate;
618
619        /// Splits the string off at the given index, returning the tail as a new string.
620        fn split_off(&mut self, at: usize) -> String = String::split_off;
621    }
622}
623
624impl<W> WritableStringExt for W where W: Writable<Target = String> {}
625
626/// An extension trait for [`Writable<HashMap<K, V, H>>`] that provides some convenience methods.
627pub trait WritableHashMapExt<K: 'static, V: 'static, H: 'static>:
628    Writable<Target = HashMap<K, V, H>>
629{
630    ext_methods! {
631        /// Clears the map, removing all key-value pairs.
632        fn clear(&mut self) = HashMap::clear;
633
634        /// Retains only the key-value pairs that match the given predicate.
635        fn retain(&mut self, f: impl FnMut(&K, &mut V) -> bool) = HashMap::retain;
636    }
637
638    /// Inserts a key-value pair into the map. If the key was already present, the old value is returned.
639    #[track_caller]
640    fn insert(&mut self, k: K, v: V) -> Option<V>
641    where
642        K: std::cmp::Eq + std::hash::Hash,
643        H: std::hash::BuildHasher,
644    {
645        self.with_mut(|map: &mut HashMap<K, V, H>| map.insert(k, v))
646    }
647
648    /// Extends the map with the key-value pairs from the given iterator.
649    #[track_caller]
650    fn extend(&mut self, iter: impl IntoIterator<Item = (K, V)>)
651    where
652        K: std::cmp::Eq + std::hash::Hash,
653        H: std::hash::BuildHasher,
654    {
655        self.with_mut(|map: &mut HashMap<K, V, H>| map.extend(iter))
656    }
657
658    /// Removes a key from the map, returning the value at the key if the key was previously in the map.
659    #[track_caller]
660    fn remove(&mut self, k: &K) -> Option<V>
661    where
662        K: std::cmp::Eq + std::hash::Hash,
663        H: std::hash::BuildHasher,
664    {
665        self.with_mut(|map: &mut HashMap<K, V, H>| map.remove(k))
666    }
667
668    /// Get a mutable reference to the value at the given key.
669    #[track_caller]
670    fn get_mut(&mut self, k: &K) -> Option<WritableRef<'_, Self, V>>
671    where
672        K: std::cmp::Eq + std::hash::Hash,
673        H: std::hash::BuildHasher,
674    {
675        WriteLock::filter_map(self.write(), |map: &mut HashMap<K, V, H>| map.get_mut(k))
676    }
677}
678
679impl<K: 'static, V: 'static, H: 'static, R> WritableHashMapExt<K, V, H> for R where
680    R: Writable<Target = HashMap<K, V, H>>
681{
682}
683
684/// An extension trait for [`Writable<HashSet<V, H>>`] that provides some convenience methods.
685pub trait WritableHashSetExt<V: 'static, H: 'static>: Writable<Target = HashSet<V, H>> {
686    ext_methods! {
687        /// Clear the hash set.
688        fn clear(&mut self) = HashSet::clear;
689
690        /// Retain only the elements specified by the predicate.
691        fn retain(&mut self, f: impl FnMut(&V) -> bool) = HashSet::retain;
692    }
693
694    /// Inserts a value into the set. Returns true if the value was not already present.
695    #[track_caller]
696    fn insert(&mut self, k: V) -> bool
697    where
698        V: std::cmp::Eq + std::hash::Hash,
699        H: std::hash::BuildHasher,
700    {
701        self.with_mut(|set| set.insert(k))
702    }
703
704    /// Extends the set with the values from the given iterator.
705    #[track_caller]
706    fn extend(&mut self, iter: impl IntoIterator<Item = V>)
707    where
708        V: std::cmp::Eq + std::hash::Hash,
709        H: std::hash::BuildHasher,
710    {
711        self.with_mut(|set| set.extend(iter))
712    }
713
714    /// Removes a value from the set. Returns true if the value was present.
715    #[track_caller]
716    fn remove(&mut self, k: &V) -> bool
717    where
718        V: std::cmp::Eq + std::hash::Hash,
719        H: std::hash::BuildHasher,
720    {
721        self.with_mut(|set| set.remove(k))
722    }
723}
724
725impl<V: 'static, H: 'static, R> WritableHashSetExt<V, H> for R where
726    R: Writable<Target = HashSet<V, H>>
727{
728}