hashconsing/
lib.rs

1// See the LICENSE files at the top-level directory of this distribution.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Hash consing library.
10//!
11//! This library is based on [Type-Safe Modular Hash-Consing][paper] by Filiâtre and Conchon. It is
12//! probably less efficient as uses Rust's `HashMap`s, not a custom built structure.
13//!
14//! If you are not familiar with hashconsing, see the [example](#example) below or read the paper.
15//!
16//! Provides constant time comparison and perfect (maximal) sharing assuming only one
17//! consign/factory is created for a given type. This assumption **must never be falsified** unless
18//! you really, **really** know what you are doing.
19//!
20//! Hash consed elements are immutable and therefore thread-safe: `HConsed` implements `Send` and
21//! `Sync`.
22//!
23//! The consign actually stores weak references to values. This ensures that values are dropped
24//! once they are not used anymore.
25//!
26//!
27//! # Example
28//!
29//! Simple example for lambda calculus from [the paper][paper].
30//!
31//! Hashconsing consists in wrapping some tree-like datatype in an immutable container, which in
32//! the case of `hashconsing` is [`HConsed`]. In this example we'll call the tree-like datatype
33//! `ActualTerm` and the hashconsed version `Term`.
34//!
35//! A `Term` is created from an `ActualTerm` by a *factory*, which `hashconsing` calls a *consign*
36//! (see [`HConsign`]). The functions for doing so are in the [`HashConsign` trait]. The idea is
37//! that the consign is a map from actual terms to `Arc`s of hashconsed terms. When given an actual
38//! term, the consign checks whether there's already a hashconsed term for it. If not, then it
39//! creates one, memorizes it and returns that. Otherwise it clones the existing one. Hence subterm
40//! sharing is maximal/perfect.
41//!
42//! A `HConsed<T>` is exactly two things: a unique identifier `uid` and an `Arc` to the real term
43//! it represents. (Hence, cloning a hashconsed term is cheap.) This library guarantees that two
44//! hashconsed terms refer to structurally identical real terms **iff** their `uid`s are equal.
45//! Hence, equality checking is constant time.
46//!
47//! ```rust
48//! extern crate hashconsing ;
49//! use hashconsing::{ HConsed, HashConsign, HConsign } ;
50//!
51//! type Term = HConsed<ActualTerm> ;
52//!
53//! #[derive(Debug, Hash, Clone, PartialEq, Eq)]
54//! enum ActualTerm {
55//!     Var(usize),
56//!     Lam(Term),
57//!     App(Term, Term)
58//! }
59//! use self::ActualTerm::* ;
60//!
61//! fn main() {
62//!     let mut factory: HConsign<ActualTerm> = HConsign::empty() ;
63//!
64//!     assert_eq! { factory.len(), 0 }
65//!
66//!     let v = factory.mk( Var(0) ) ;
67//!     assert_eq! { factory.len(), 1 }
68//!
69//!     let v2 = factory.mk( Var(3) ) ;
70//!     assert_eq! { factory.len(), 2 }
71//!
72//!     let lam = factory.mk(
73//!         Lam( v2.clone() )
74//!     ) ;
75//!     assert_eq! { factory.len(), 3 }
76//!
77//!     let v3 = factory.mk( Var(3) ) ;
78//!     // v2 is the same as v3: Var(3). Consign has not created anything new, and
79//!     // v2 and v3 are conceptually the same term.
80//!     assert_eq! { factory.len(), 3 }
81//!     assert_eq! { v2.uid(), v3.uid() }
82//!     assert_eq! { v2.get(), v3.get() }
83//!     assert_eq! { v2,       v3       }
84//!
85//!     let lam2 = factory.mk( Lam(v3) ) ;
86//!     // Not new either.
87//!     assert_eq! { factory.len(), 3 }
88//!     assert_eq! { lam.uid(), lam2.uid() }
89//!     assert_eq! { lam.get(), lam2.get() }
90//!     assert_eq! { lam,       lam2       }
91//!
92//!     let app = factory.mk( App(lam2, v) ) ;
93//!     assert_eq! { factory.len(), 4 }
94//! }
95//! ```
96//!
97//! This library maintains the invariant stated above as long as you **never create two consigns
98//! for the same type**.
99//!
100//! Users are free to use the consign however they see fit: one can create a factory directly as in
101//! the example above, but passing it around everywhere it's needed is tedious. The author
102//! recommends the following workflow instead. It relies on the [`consign`] macro which creates
103//! a [lazy static] factory protected by a `RwLock` for thread-safety. The consign and the
104//! constructors are wrapped in an appropriately named module. The consign is invisible and
105//! creating terms is easy.
106//!
107//! ```rust
108//! #[macro_use]
109//! extern crate hashconsing ;
110//!
111//! pub mod term {
112//!     use hashconsing::{ HConsed, HashConsign } ;
113//!     pub type Term = HConsed<ActualTerm> ;
114//!     #[derive(Debug, Hash, Clone, PartialEq, Eq)]
115//!     pub enum ActualTerm {
116//!         Var(usize),
117//!         Lam(Term),
118//!         App(Term, Term)
119//!     }
120//!
121//!     consign! {
122//!         /// Factory for terms.
123//!         let factory = consign(37) for ActualTerm ;
124//!     }
125//!     pub fn var(v: usize) -> Term {
126//!         factory.mk( ActualTerm::Var(v) )
127//!     }
128//!     pub fn lam(t: Term) -> Term {
129//!         factory.mk( ActualTerm::Lam(t) )
130//!     }
131//!     pub fn app(t_1: Term, t_2: Term) -> Term {
132//!         factory.mk( ActualTerm::App(t_1, t_2) )
133//!     }
134//! }
135//!
136//! fn main() {
137//!     let v = term::var(0) ;
138//!     let v2 = term::var(3) ;
139//!     let lam = term::lam(v2) ;
140//!     let v3 = term::var(3) ;
141//!     let lam2 = term::lam(v3) ;
142//!     let app = term::app(lam2, v) ;
143//! }
144//! ```
145//!
146//! Note that `HConsed<T>` `Deref`s to `T`, so you don't need to extend `HConsed<T>` using some
147//! trait to add the functions you need. Just implement them for `T`. Functions taking an `& mut
148//! self` won't work since `HConsed<T>` gives you access to `T` through an `Arc`.
149//!
150//! ```rust
151//! # extern crate hashconsing ;
152//! # pub mod term {
153//! #     use hashconsing::* ;
154//! #     pub type Term = HConsed<ActualTerm> ;
155//! #     #[derive(Debug, Hash, Clone, PartialEq, Eq)]
156//! #     pub enum ActualTerm {
157//! #         Var(usize),
158//! #         Lam(Term),
159//! #         App(Term, Term)
160//! #     }
161//! #
162//! #     consign! {
163//! #         /// Factory for terms.
164//! #         let factory = consign(37) for ActualTerm ;
165//! #     }
166//! #     pub fn var(v: usize) -> Term {
167//! #         factory.mk( ActualTerm::Var(v) )
168//! #     }
169//! #     pub fn lam(t: Term) -> Term {
170//! #         factory.mk( ActualTerm::Lam(t) )
171//! #     }
172//! #     pub fn app(t_1: Term, t_2: Term) -> Term {
173//! #         factory.mk( ActualTerm::App(t_1, t_2) )
174//! #     }
175//! impl std::fmt::Display for ActualTerm {
176//!     fn fmt(& self, fmt: & mut std::fmt::Formatter) -> std::fmt::Result {
177//!         match self {
178//!             ActualTerm::Var(i) => write!(fmt, "v{}", i),
179//!             ActualTerm::Lam(t) => write!(fmt, "({})", t.get()),
180//!             ActualTerm::App(u, v) => write!(
181//!                 fmt, "{}.{}", u.get(), v.get()
182//!             ),
183//!         }
184//!     }
185//! }
186//! # }
187//! fn main() {
188//!     let v = term::var(0) ;
189//!     let v3 = term::var(3) ;
190//!     let lam2 = term::lam(v3) ;
191//!     let app = term::app(lam2, v) ;
192//!     assert_eq! { & format!("{}", app), "(v3).v0" }
193//! }
194//! ```
195//!
196//! # Collections with trivial hash function
197//!
198//! This library provides two special collections: [`HConSet`] and [`HConMap`]. They use the
199//! trivial hash function over hashconsed values' unique identifier. [Read more.][coll mod]
200//!
201//! Another way to have efficient sets/maps of/from hashconsed things is to use the `BTree` sets
202//! and maps from the standard library.
203//!
204//! [paper]: http://dl.acm.org/citation.cfm?doid=1159876.1159880
205//! (Type-safe modular hash-consing)
206//! [`HConsed`]: trait.HashConsed.html (HConsed type)
207//! [`HConSet`]: coll/struct.HConSet.html (HConSet documentation)
208//! [`HConMap`]: coll/struct.HConMap.html (HConMap documentation)
209//! [`HashConsign` trait]: trait.HashConsign.html (HashConsign trait)
210//! [`HConsign`]: struct.HConsign.html (HConsign type)
211//! [`consign`]: macro.consign.html (consign macro)
212//! [coll mod]: coll/index.html (coll module documentation)
213//! [lazy static]: https://crates.io/crates/lazy_static
214//! (lazy_static library on crates.io)
215
216#![deny(warnings)]
217
218use std::{
219    cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
220    collections::{hash_map::RandomState, HashMap},
221    fmt,
222    hash::{BuildHasher, Hash, Hasher},
223    ops::Deref,
224    sync::{Arc, RwLock, Weak},
225};
226
227pub extern crate lazy_static;
228
229#[cfg(test)]
230mod test;
231
232/// Creates a lazy static consign.
233///
234/// The consign is protected by a `RwLock`.
235///
236/// Arguments:
237/// - `$(#[$meta:meta])*` meta stuff, typically comments ;
238/// - `$name:ident` name of the consign ;
239/// - `$capa:expr` initial capacity when creating the consign ;
240/// - `$hash_builder:expr` optional hash builder, an
241///   implementation of [`std::hash::BuildHasher`] ;
242/// - `$typ:typ,` type being hashconsed (the underlying type, not the
243///     hashconsed one) ;
244#[macro_export]
245macro_rules! consign {
246    (
247        $(#[$meta:meta])*
248        let $name:ident = consign($capa:expr) for $typ:ty ;
249    ) => (
250        $crate::lazy_static::lazy_static! {
251            $(#[$meta])*
252            static ref $name: std::sync::RwLock<
253                $crate::HConsign<$typ>
254            > = std::sync::RwLock::new(
255                $crate::HConsign::with_capacity( $capa )
256            );
257        }
258    );
259    (
260        $(#[$meta:meta])*
261        let $name:ident = consign($capa:expr, $hash_builder:expr) for $typ:ty ;
262    ) => (
263        $crate::lazy_static::lazy_static! {
264            $(#[$meta])*
265            static ref $name: std::sync::RwLock<
266                $crate::HConsign<$typ>
267            > = std::sync::RwLock::new(
268                $crate::HConsign::with_capacity_and_hasher( $capa, $hash_builder )
269            );
270        }
271    );
272}
273
274#[deprecated(
275    since = "1.4.0",
276    note = "Deprecated for poor performance. We recommend the hash_coll module instead."
277)]
278pub mod coll;
279pub mod hash_coll;
280
281/// Internal trait used to recognize hashconsed things.
282///
283/// The only purpose of this trait (currently) is to simplify the type
284/// signature of the collections of hashconsed things.
285pub trait HashConsed {
286    /// Elements stored inside a hashconsed value.
287    type Inner;
288}
289
290/// A hashconsed value.
291pub struct HConsed<T> {
292    /// The actual element.
293    elm: Arc<T>,
294    /// Unique identifier of the element.
295    uid: u64,
296}
297impl<T> HashConsed for HConsed<T> {
298    type Inner = T;
299}
300
301impl<T> HConsed<T> {
302    /// The inner element. Can also be accessed *via* dereferencing.
303    #[inline]
304    pub fn get(&self) -> &T {
305        self.elm.deref()
306    }
307    /// The unique identifier of the element.
308    #[inline]
309    pub fn uid(&self) -> u64 {
310        self.uid
311    }
312    /// Turns a hashconsed thing in a weak hashconsed thing.
313    #[inline]
314    pub fn to_weak(&self) -> WHConsed<T> {
315        WHConsed {
316            elm: Arc::downgrade(&self.elm),
317            uid: self.uid,
318        }
319    }
320
321    /// Weak reference version.
322    pub fn to_weak_ref(&self) -> Weak<T> {
323        Arc::downgrade(&self.elm)
324    }
325
326    /// Number of (strong) references to this term.
327    pub fn arc_count(&self) -> usize {
328        Arc::strong_count(&self.elm)
329    }
330}
331
332impl<T: fmt::Debug> fmt::Debug for HConsed<T> {
333    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
334        write!(fmt, "{:?}", self.elm)
335    }
336}
337
338impl<T> Clone for HConsed<T> {
339    fn clone(&self) -> Self {
340        HConsed {
341            elm: self.elm.clone(),
342            uid: self.uid,
343        }
344    }
345}
346
347impl<T> PartialEq for HConsed<T> {
348    #[inline]
349    fn eq(&self, rhs: &Self) -> bool {
350        self.uid == rhs.uid
351    }
352}
353impl<T> Eq for HConsed<T> {}
354impl<T> PartialOrd for HConsed<T> {
355    #[inline]
356    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
357        self.uid.partial_cmp(&other.uid)
358    }
359}
360impl<T> Ord for HConsed<T> {
361    #[inline]
362    fn cmp(&self, other: &Self) -> Ordering {
363        self.uid.cmp(&other.uid)
364    }
365}
366impl<T: Hash> Hash for HConsed<T> {
367    #[inline]
368    fn hash<H>(&self, state: &mut H)
369    where
370        H: Hasher,
371    {
372        self.uid.hash(state)
373    }
374}
375
376impl<T> Deref for HConsed<T> {
377    type Target = T;
378    #[inline]
379    fn deref(&self) -> &T {
380        self.elm.deref()
381    }
382}
383
384impl<T: fmt::Display> fmt::Display for HConsed<T> {
385    #[inline]
386    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
387        self.elm.fmt(fmt)
388    }
389}
390
391/// Weak version of `HConsed`.
392pub struct WHConsed<T> {
393    /// The actual element.
394    elm: Weak<T>,
395    /// Unique identifier of the element.
396    uid: u64,
397}
398impl<T> WHConsed<T> {
399    /// Turns a weak hashconsed thing in a hashconsed thing.
400    pub fn to_hconsed(&self) -> Option<HConsed<T>> {
401        self.elm.upgrade().map(|arc| HConsed {
402            elm: arc,
403            uid: self.uid,
404        })
405    }
406
407    /// A reference to the underlying weak reference.
408    pub fn as_weak_ref(&self) -> &Weak<T> {
409        &self.elm
410    }
411}
412
413impl<T: fmt::Display> fmt::Display for WHConsed<T> {
414    #[inline]
415    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
416        if let Some(arc) = self.elm.upgrade() {
417            arc.fmt(fmt)
418        } else {
419            write!(fmt, "<freed>")
420        }
421    }
422}
423
424impl<T> Hash for WHConsed<T> {
425    #[inline]
426    fn hash<H>(&self, state: &mut H)
427    where
428        H: Hasher,
429    {
430        self.uid.hash(state)
431    }
432}
433
434impl<T> PartialEq for WHConsed<T> {
435    #[inline]
436    fn eq(&self, rhs: &Self) -> bool {
437        self.uid == rhs.uid
438    }
439}
440impl<T> Eq for WHConsed<T> {}
441impl<T> PartialOrd for WHConsed<T> {
442    #[inline]
443    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
444        self.uid.partial_cmp(&other.uid)
445    }
446}
447impl<T> Ord for WHConsed<T> {
448    #[inline]
449    fn cmp(&self, other: &Self) -> Ordering {
450        self.uid.cmp(&other.uid)
451    }
452}
453
454/// The consign storing the actual hash consed elements as `HConsed`s.
455pub struct HConsign<T: Hash + Eq + Clone, S = RandomState> {
456    /// The actual hash consing table.
457    table: HashMap<T, WHConsed<T>, S>,
458    /// Counter for uids.
459    count: u64,
460}
461
462impl<T: Hash + Eq + Clone> HConsign<T, RandomState> {
463    /// Creates an empty consign.
464    #[inline]
465    pub fn empty() -> Self {
466        HConsign {
467            table: HashMap::new(),
468            count: 0,
469        }
470    }
471
472    /// Creates an empty consign with a capacity.
473    #[inline]
474    pub fn with_capacity(capacity: usize) -> Self {
475        HConsign {
476            table: HashMap::with_capacity(capacity),
477            count: 0,
478        }
479    }
480}
481
482impl<T: Hash + Eq + Clone, S> HConsign<T, S> {
483    /// Fold on the elements stored in the consign.
484    #[inline]
485    pub fn fold<Acc, F>(&self, mut init: Acc, mut f: F) -> Acc
486    where
487        F: FnMut(Acc, HConsed<T>) -> Acc,
488    {
489        for weak in self.table.values() {
490            if let Some(consed) = weak.to_hconsed() {
491                init = f(init, consed)
492            }
493        }
494        init
495    }
496
497    /// Fold on the elements stored in the consign, result version.
498    #[inline]
499    pub fn fold_res<Acc, F, E>(&self, mut init: Acc, mut f: F) -> Result<Acc, E>
500    where
501        F: FnMut(Acc, HConsed<T>) -> Result<Acc, E>,
502    {
503        for weak in self.table.values() {
504            if let Some(consed) = weak.to_hconsed() {
505                init = f(init, consed)?
506            }
507        }
508        Ok(init)
509    }
510
511    /// The number of elements stored, mostly for testing.
512    #[inline]
513    pub fn len(&self) -> usize {
514        self.table.len()
515    }
516    /// Capacity of the underlying hashtable, mostly for testing.
517    #[inline]
518    pub fn capacity(&self) -> usize {
519        self.table.capacity()
520    }
521
522    /// True if the consign is empty.
523    #[inline]
524    pub fn is_empty(&self) -> bool {
525        self.table.is_empty()
526    }
527}
528
529impl<T: Hash + Eq + Clone, S: BuildHasher> HConsign<T, S> {
530    /// Creates an empty consign with a custom hash
531    #[inline]
532    pub fn with_hasher(build_hasher: S) -> Self {
533        HConsign {
534            table: HashMap::with_hasher(build_hasher),
535            count: 0,
536        }
537    }
538
539    /// Creates an empty consign with a capacity.
540    #[inline]
541    pub fn with_capacity_and_hasher(capacity: usize, build_hasher: S) -> Self {
542        HConsign {
543            table: HashMap::with_capacity_and_hasher(capacity, build_hasher),
544            count: 0,
545        }
546    }
547
548    /// Inserts in the consign.
549    ///
550    /// One of the following must hold:
551    ///
552    /// - `self.table` is not defined at `key`
553    /// - the weak ref in `self.table` at `key` returns `None` when upgraded.
554    ///
555    /// This is checked in `debug` but not `release`.
556    #[inline]
557    fn insert(&mut self, key: T, wconsed: WHConsed<T>) {
558        let prev = self.table.insert(key, wconsed);
559        debug_assert!(match prev {
560            None => true,
561            Some(prev) => prev.to_hconsed().is_none(),
562        })
563    }
564
565    /// Attempts to retrieve an *upgradable* value from the map.
566    #[inline]
567    fn get(&self, key: &T) -> Option<HConsed<T>> {
568        if let Some(old) = self.table.get(key) {
569            old.to_hconsed()
570        } else {
571            None
572        }
573    }
574}
575
576impl<T: Hash + Eq + Clone, S> fmt::Display for HConsign<T, S>
577where
578    T: Hash + fmt::Display,
579{
580    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
581        write!(fmt, "consign:")?;
582        for e in self.table.values() {
583            write!(fmt, "\n  | {e}")?;
584        }
585        Ok(())
586    }
587}
588
589/// HConsed element creation.
590///
591/// Implemented *via* a trait to be able to extend `RwLock` for lazy static
592/// consigns.
593pub trait HashConsign<T: Hash>: Sized {
594    /// Hashconses something and returns the hash consed version.
595    ///
596    /// Returns `true` iff the element
597    ///
598    /// - was not in the consign at all, or
599    /// - was in the consign but it is not referenced (weak ref cannot be
600    ///   upgraded.)
601    fn mk_is_new(self, elm: T) -> (HConsed<T>, bool);
602
603    /// Creates a HConsed element.
604    fn mk(self, elm: T) -> HConsed<T> {
605        self.mk_is_new(elm).0
606    }
607
608    /// Removes *dead* elements from the consign.
609    ///
610    /// An element is *dead* if it is not being referenced outside of the consign, meaning it is not
611    /// reachable anymore.
612    fn collect(self);
613
614    /// Shrinks the capacity of the consign as much as possible.
615    fn shrink_to_fit(self);
616
617    /// Calls [`collect`](#tymethod.collect) and [`shrink_to_fit`](#tymethod.shrink_to_fit).
618    fn collect_to_fit(self);
619
620    /// Reserves capacity for at least `additional` more elements.
621    fn reserve(self, additional: usize);
622}
623impl<'a, T: Hash + Eq + Clone, S: BuildHasher> HashConsign<T> for &'a mut HConsign<T, S> {
624    fn mk_is_new(self, elm: T) -> (HConsed<T>, bool) {
625        // If the element is known and upgradable return it.
626        if let Some(hconsed) = self.get(&elm) {
627            debug_assert!(*hconsed.elm == elm);
628            return (hconsed, false);
629        }
630        // Otherwise build hconsed version.
631        let hconsed = HConsed {
632            elm: Arc::new(elm.clone()),
633            uid: self.count,
634        };
635        // Increment uid count.
636        self.count += 1;
637        // ...add weak version to the table...
638        self.insert(elm, hconsed.to_weak());
639        // ...and return consed version.
640        (hconsed, true)
641    }
642
643    fn collect(self) {
644        let mut old_len = self.table.len() + 1;
645        let mut max_uid = None;
646        while old_len != self.table.len() {
647            old_len = self.table.len();
648            max_uid = None;
649
650            self.table.retain(|_key, val| {
651                if val.elm.strong_count() > 0 {
652                    let max = max_uid.get_or_insert(val.uid);
653                    if *max < val.uid {
654                        *max = val.uid
655                    }
656                    true
657                } else {
658                    false
659                }
660            });
661        }
662        if self.table.is_empty() {
663            self.count = 0
664        } else if let Some(max) = max_uid {
665            self.count = max + 1
666        }
667    }
668
669    fn shrink_to_fit(self) {
670        self.table.shrink_to_fit()
671    }
672
673    fn collect_to_fit(self) {
674        self.collect();
675        self.shrink_to_fit();
676    }
677
678    fn reserve(self, additional: usize) {
679        self.table.reserve(additional)
680    }
681}
682
683/// Helper macro to get a read or write version of a locked consign.
684macro_rules! get {
685    { read on $consign:expr } => {
686        get! { @expect $consign.read() }
687    };
688    { write on $consign:expr } => {
689        get! { @expect $consign.write() }
690    };
691    { @expect $e:expr } => {
692        $e.expect("[hashconsing] global consign is poisoned")
693    };
694}
695
696impl<'a, T: Hash + Eq + Clone> HashConsign<T> for &'a RwLock<HConsign<T>> {
697    /// If the element is already in the consign, only read access will be
698    /// requested.
699    fn mk_is_new(self, elm: T) -> (HConsed<T>, bool) {
700        // Request read and check if element already exists.
701        {
702            let slf = get!(read on self);
703            // If the element is known and upgradable return it.
704            if let Some(hconsed) = slf.get(&elm) {
705                debug_assert!(*hconsed.elm == elm);
706                return (hconsed, false);
707            }
708        };
709        // Otherwise get mutable `self`.
710        let mut slf = get!(write on self);
711
712        // Someone might have inserted since we checked, check again.
713        if let Some(hconsed) = slf.get(&elm) {
714            debug_assert!(*hconsed.elm == elm);
715            return (hconsed, false);
716        }
717
718        // Otherwise build hconsed version.
719        let hconsed = HConsed {
720            elm: Arc::new(elm.clone()),
721            uid: slf.count,
722        };
723        // Increment uid count.
724        slf.count += 1;
725        // ...add weak version to the table...
726        slf.insert(elm, hconsed.to_weak());
727        // ...and return consed version.
728        (hconsed, true)
729    }
730
731    fn collect(self) {
732        get!(write on self).collect()
733    }
734    fn shrink_to_fit(self) {
735        get!(write on self).shrink_to_fit()
736    }
737    fn collect_to_fit(self) {
738        get!(write on self).collect_to_fit()
739    }
740    fn reserve(self, additional: usize) {
741        get!(write on self).reserve(additional)
742    }
743}