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}