| //! 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 |
| } |
| } |