Skip to main content

rustc_data_structures/
intern.rs

1use std::fmt::{self, Debug};
2use std::hash::{Hash, Hasher};
3use std::ops::Deref;
4use std::ptr;
5
6use crate::stable_hash::{StableHash, StableHashCtxt, StableHasher};
7
8mod private {
9    #[derive(#[automatically_derived]
impl ::core::clone::Clone for PrivateZst {
    #[inline]
    fn clone(&self) -> PrivateZst { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for PrivateZst { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for PrivateZst {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "PrivateZst")
    }
}Debug)]
10    pub struct PrivateZst;
11}
12
13/// This type is a reference with one special behaviour: the reference pointer (i.e. the address of
14/// the value referred to) is used for equality and hashing, rather than the value's contents, as
15/// would occur with a vanilla reference. There are two cases when this is useful.
16///
17/// - Types where uniqueness is guaranteed. This is most commonly achieved via interning -- hence
18///   the name `Interned` -- though it may also be possible via other means. In this case, the use
19///   of `Interned` is primarily a performance optimization, because pointer equality/hashing gives
20///   the same results as value equality/hashing, but is faster. (The use of the `Interned` type
21///   also provides documentation about the interned-ness.)
22///
23///   Note that in this case it is possible to have a `T` and a `Interned<T>` that are (or refer
24///   to) equal but different values. But if you have two different `Interned<T>`s, they both refer
25///   to the same value, at a single location in memory.
26///
27/// - Types with identity, where distinct values should always be considered unequal, even if they
28///   have equal values. These are rare in Rust, but do occur sometimes. In this case, the use of
29///   `Interned` gives different behaviour, because pointer equality/hashing gives different result
30///   to value equality/hashing, and is also faster.
31///
32/// The `PrivateZst` field means you can pattern match with `Interned(v, _)` but you can only
33/// construct a `Interned` with `new_unchecked`, and not directly. This means that all creation
34/// points can be audited easily.
35#[rustc_pass_by_value]
36pub struct Interned<'a, T>(pub &'a T, pub private::PrivateZst);
37
38impl<'a, T> Interned<'a, T> {
39    /// Create a new `Interned` value. The value referred to *must* satisfy one of the following
40    /// two conditions.
41    /// - It must be unique and it must remain unique in the future.
42    /// - It must be of a type with "identity" such that distinct values should always be
43    ///   considered unequal.
44    ///
45    /// This function has `_unchecked` in the name but is not `unsafe`, because if neither of these
46    /// conditions is met it will cause incorrect behaviour but will not affect memory safety.
47    #[inline]
48    pub const fn new_unchecked(t: &'a T) -> Self {
49        Interned(t, private::PrivateZst)
50    }
51}
52
53impl<'a, T> Clone for Interned<'a, T> {
54    fn clone(&self) -> Self {
55        *self
56    }
57}
58
59impl<'a, T> Copy for Interned<'a, T> {}
60
61impl<'a, T> Deref for Interned<'a, T> {
62    type Target = T;
63
64    #[inline]
65    fn deref(&self) -> &T {
66        self.0
67    }
68}
69
70impl<'a, T> PartialEq for Interned<'a, T> {
71    #[inline]
72    fn eq(&self, other: &Self) -> bool {
73        // Pointer equality implies equality, due to the uniqueness constraint.
74        ptr::eq(self.0, other.0)
75    }
76}
77
78impl<'a, T> Eq for Interned<'a, T> {}
79
80impl<'a, T> Hash for Interned<'a, T> {
81    #[inline]
82    fn hash<H: Hasher>(&self, s: &mut H) {
83        // Pointer hashing is sufficient.
84        ptr::hash(self.0, s)
85    }
86}
87
88impl<T> StableHash for Interned<'_, T>
89where
90    T: StableHash,
91{
92    fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHasher) {
93        self.0.stable_hash(hcx, hasher);
94    }
95}
96
97impl<T: Debug> Debug for Interned<'_, T> {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        self.0.fmt(f)
100    }
101}
102
103#[cfg(test)]
104mod tests;