rustc_data_structures/frozen.rs
1//! An immutable, owned value (except for interior mutability).
2//!
3//! The purpose of `Frozen` is to make a value immutable for the sake of defensive programming. For example,
4//! suppose we have the following:
5//!
6//! ```rust
7//! struct Bar { /* some data */ }
8//!
9//! struct Foo {
10//! /// Some computed data that should never change after construction.
11//! pub computed: Bar,
12//!
13//! /* some other fields */
14//! }
15//!
16//! impl Bar {
17//! /// Mutate the `Bar`.
18//! pub fn mutate(&mut self) { }
19//! }
20//! ```
21//!
22//! Now suppose we want to pass around a mutable `Foo` instance but, we want to make sure that
23//! `computed` does not change accidentally (e.g. somebody might accidentally call
24//! `foo.computed.mutate()`). This is what `Frozen` is for. We can do the following:
25//!
26//! ```
27//! # struct Bar {}
28//! use rustc_data_structures::frozen::Frozen;
29//!
30//! struct Foo {
31//! /// Some computed data that should never change after construction.
32//! pub computed: Frozen<Bar>,
33//!
34//! /* some other fields */
35//! }
36//! ```
37//!
38//! `Frozen` impls `Deref`, so we can ergonomically call methods on `Bar`, but it doesn't `impl
39//! DerefMut`. Now calling `foo.compute.mutate()` will result in a compile-time error stating that
40//! `mutate` requires a mutable reference but we don't have one.
41//!
42//! # Caveats
43//!
44//! - `Frozen` doesn't try to defend against interior mutability (e.g. `Frozen<RefCell<Bar>>`).
45//! - `Frozen` doesn't pin it's contents (e.g. one could still do `foo.computed =
46//! Frozen::freeze(new_bar)`).
47
48/// An owned immutable value.
49#[derive(Debug)]
50pub struct Frozen<T>(T);
51
52impl<T> Frozen<T> {
53 pub fn freeze(val: T) -> Self {
54 Frozen(val)
55 }
56}
57
58impl<T> std::ops::Deref for Frozen<T> {
59 type Target = T;
60
61 fn deref(&self) -> &T {
62 &self.0
63 }
64}