summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_data_structures/src/frozen.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_data_structures/src/frozen.rs')
-rw-r--r--compiler/rustc_data_structures/src/frozen.rs64
1 files changed, 64 insertions, 0 deletions
diff --git a/compiler/rustc_data_structures/src/frozen.rs b/compiler/rustc_data_structures/src/frozen.rs
new file mode 100644
index 000000000..c81e1b124
--- /dev/null
+++ b/compiler/rustc_data_structures/src/frozen.rs
@@ -0,0 +1,64 @@
+//! An immutable, owned value (except for interior mutability).
+//!
+//! The purpose of `Frozen` is to make a value immutable for the sake of defensive programming. For example,
+//! suppose we have the following:
+//!
+//! ```rust
+//! struct Bar { /* some data */ }
+//!
+//! struct Foo {
+//! /// Some computed data that should never change after construction.
+//! pub computed: Bar,
+//!
+//! /* some other fields */
+//! }
+//!
+//! impl Bar {
+//! /// Mutate the `Bar`.
+//! pub fn mutate(&mut self) { }
+//! }
+//! ```
+//!
+//! Now suppose we want to pass around a mutable `Foo` instance but, we want to make sure that
+//! `computed` does not change accidentally (e.g. somebody might accidentally call
+//! `foo.computed.mutate()`). This is what `Frozen` is for. We can do the following:
+//!
+//! ```
+//! # struct Bar {}
+//! use rustc_data_structures::frozen::Frozen;
+//!
+//! struct Foo {
+//! /// Some computed data that should never change after construction.
+//! pub computed: Frozen<Bar>,
+//!
+//! /* some other fields */
+//! }
+//! ```
+//!
+//! `Frozen` impls `Deref`, so we can ergonomically call methods on `Bar`, but it doesn't `impl
+//! DerefMut`. Now calling `foo.compute.mutate()` will result in a compile-time error stating that
+//! `mutate` requires a mutable reference but we don't have one.
+//!
+//! # Caveats
+//!
+//! - `Frozen` doesn't try to defend against interior mutability (e.g. `Frozen<RefCell<Bar>>`).
+//! - `Frozen` doesn't pin it's contents (e.g. one could still do `foo.computed =
+//! Frozen::freeze(new_bar)`).
+
+/// An owned immutable value.
+#[derive(Debug)]
+pub struct Frozen<T>(T);
+
+impl<T> Frozen<T> {
+ pub fn freeze(val: T) -> Self {
+ Frozen(val)
+ }
+}
+
+impl<T> std::ops::Deref for Frozen<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.0
+ }
+}