64 lines
1.8 KiB
Rust
64 lines
1.8 KiB
Rust
//! 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:
|
|
//!
|
|
//! ```rust
|
|
//! 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
|
|
}
|
|
}
|