diff options
| author | Brian Anderson <banderson@mozilla.com> | 2014-05-18 21:47:51 -0700 |
|---|---|---|
| committer | Brian Anderson <banderson@mozilla.com> | 2014-05-20 10:38:21 -0700 |
| commit | 8f2a2e2dd8320215f834376c9a8a237e285df64e (patch) | |
| tree | b57516975f4df3d225a3f5fbb602f4ce448b4fbc /src/libcore | |
| parent | 629195582b39a4956cd62439911f0094cf3878c6 (diff) | |
| download | rust-8f2a2e2dd8320215f834376c9a8a237e285df64e.tar.gz rust-8f2a2e2dd8320215f834376c9a8a237e285df64e.zip | |
core: Improve docs for cell
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/cell.rs | 152 |
1 files changed, 151 insertions, 1 deletions
diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index d42ad49485f..c5af72f9ccf 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -8,7 +8,157 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Types that provide interior mutability. +//! Sharable mutable containers. +//! +//! Values of the `Cell` and `RefCell` types may be mutated through +//! shared references (i.e. the common `&T` type), whereas most Rust +//! types can only be mutated through unique (`&mut T`) references. We +//! say that `Cell` and `RefCell` provide *interior mutability*, in +//! contrast with typical Rust types that exhibit *inherited +//! mutability*. +//! +//! Cell types come in two flavors: `Cell` and `RefCell`. `Cell` +//! provides `get` and `set` methods that change the +//! interior value with a single method call. `Cell` though is only +//! compatible with types that implement `Copy`. For other types, +//! one must use the `RefCell` type, acquiring a write lock before +//! mutating. +//! +//! `RefCell` uses Rust's lifetimes to implement *dynamic borrowing*, +//! a process whereby one can claim temporary, exclusive, mutable +//! access to the inner value. Borrows for `RefCell`s are tracked *at +//! runtime*, unlike Rust's native reference types which are entirely +//! tracked statically, at compile time. Because `RefCell` borrows are +//! dynamic it is possible to attempt to borrow a value that is +//! already mutably borrowed; when this happens it results in task +//! failure. +//! +//! # When to choose interior mutability +//! +//! The more common inherited mutability, where one must have unique +//! access to mutate a value, is one of the key language elements that +//! enables Rust to reason strongly about pointer aliasing, statically +//! preventing crash bugs. Because of that, inherited mutability is +//! preferred, and interior mutability is something of a last +//! resort. Since cell types enable mutation where it would otherwise +//! be disallowed though, there are occassions when interior +//! mutability might be appropriate, or even *must* be used, e.g. +//! +//! * Introducing inherited mutability roots to shared types. +//! * Implementation details of logically-immutable methods. +//! * Mutating implementations of `clone`. +//! +//! ## Introducing inherited mutability roots to shared types +//! +//! Shared smart pointer types, including `Rc` and `Arc`, provide +//! containers that can be cloned and shared between multiple parties. +//! Because the contained values may be multiply-aliased, they can +//! only be borrowed as shared references, not mutable references. +//! Without cells then it would be impossible to mutate data inside of +//! shared boxes at all! +//! +//! It's very common then to put a `RefCell` inside shared pointer +//! types to reintroduce mutability: +//! +//! ``` +//! extern crate collections; +//! +//! use collections::HashMap; +//! use std::cell::RefCell; +//! use std::rc::Rc; +//! +//! fn main() { +//! let shared_map: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::new())); +//! shared_map.borrow_mut().insert("africa", 92388); +//! shared_map.borrow_mut().insert("kyoto", 11837); +//! shared_map.borrow_mut().insert("piccadilly", 11826); +//! shared_map.borrow_mut().insert("marbles", 38); +//! } +//! ``` +//! +//! ## Implementation details of logically-immutable methods +//! +//! Occasionally it may be desirable not to expose in an API that +//! there is mutation happening "under the hood". This may be because +//! logically the operation is immutable, but e.g. caching forces the +//! implementation to perform mutation; or because you must employ +//! mutation to implement a trait method that was originally defined +//! to take `&self`. +//! +//! ``` +//! extern crate collections; +//! +//! use collections::HashMap; +//! use std::cell::RefCell; +//! +//! struct Graph { +//! edges: HashMap<uint, uint>, +//! span_tree_cache: RefCell<Option<Vec<(uint, uint)>>> +//! } +//! +//! impl Graph { +//! fn minimum_spanning_tree(&self) -> Vec<(uint, uint)> { +//! // Create a new scope to contain the lifetime of the +//! // dynamic borrow +//! { +//! // Take a reference to the inside of cache cell +//! let mut cache = self.span_tree_cache.borrow_mut(); +//! if cache.is_some() { +//! return cache.take_unwrap().clone(); +//! } +//! +//! let span_tree = self.calc_span_tree(); +//! *cache = Some(span_tree); +//! } +//! +//! // Recursive call to return the just-cached value. +//! // Note that if we had not let the previous borrow +//! // of the cache fall out of scope then the subsequent +//! // recursive borrow would cause a dynamic task failure. +//! // This is the major hazard of using `RefCell`. +//! self.minimum_spanning_tree() +//! } +//! # fn calc_span_tree(&self) -> Vec<(uint, uint)> { vec!() } +//! } +//! # fn main() { } +//! ``` +//! +//! ## Mutating implementations of `clone` +//! +//! This is simply a special - bot common - case of the previous: +//! hiding mutability for operations that appear to be immutable. +//! The `clone` method is expected to not change the source value, and +//! is declared to take `&self`, not `&mut self`. Therefore any +//! mutation that happens in the `clone` method must use cell +//! types. For example, `Rc` maintains its reference counts within a +//! `Cell`. +//! +//! ``` +//! use std::cell::Cell; +//! +//! struct Rc<T> { +//! ptr: *mut RcBox<T> +//! } +//! +//! struct RcBox<T> { +//! value: T, +//! refcount: Cell<uint> +//! } +//! +//! impl<T> Clone for Rc<T> { +//! fn clone(&self) -> Rc<T> { +//! unsafe { +//! (*self.ptr).refcount.set((*self.ptr).refcount.get() + 1); +//! Rc { ptr: self.ptr } +//! } +//! } +//! } +//! ``` +//! +// TODO: Explain difference between Cell and RefCell +// TODO: Downsides to interior mutability +// TODO: Can't be shared between threads. Dynamic borrows +// TODO: Relationship to Atomic types and RWLock use clone::Clone; use cmp::Eq; |
