|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 |
| -//! Types that provide interior mutability. |
| 11 | +//! Sharable mutable containers. |
| 12 | +//! |
| 13 | +//! Values of the `Cell` and `RefCell` types may be mutated through |
| 14 | +//! shared references (i.e. the common `&T` type), whereas most Rust |
| 15 | +//! types can only be mutated through unique (`&mut T`) references. We |
| 16 | +//! say that `Cell` and `RefCell` provide *interior mutability*, in |
| 17 | +//! contrast with typical Rust types that exhibit *inherited |
| 18 | +//! mutability*. |
| 19 | +//! |
| 20 | +//! Cell types come in two flavors: `Cell` and `RefCell`. `Cell` |
| 21 | +//! provides `get` and `set` methods that change the |
| 22 | +//! interior value with a single method call. `Cell` though is only |
| 23 | +//! compatible with types that implement `Copy`. For other types, |
| 24 | +//! one must use the `RefCell` type, acquiring a write lock before |
| 25 | +//! mutating. |
| 26 | +//! |
| 27 | +//! `RefCell` uses Rust's lifetimes to implement *dynamic borrowing*, |
| 28 | +//! a process whereby one can claim temporary, exclusive, mutable |
| 29 | +//! access to the inner value. Borrows for `RefCell`s are tracked *at |
| 30 | +//! runtime*, unlike Rust's native reference types which are entirely |
| 31 | +//! tracked statically, at compile time. Because `RefCell` borrows are |
| 32 | +//! dynamic it is possible to attempt to borrow a value that is |
| 33 | +//! already mutably borrowed; when this happens it results in task |
| 34 | +//! failure. |
| 35 | +//! |
| 36 | +//! # When to choose interior mutability |
| 37 | +//! |
| 38 | +//! The more common inherited mutability, where one must have unique |
| 39 | +//! access to mutate a value, is one of the key language elements that |
| 40 | +//! enables Rust to reason strongly about pointer aliasing, statically |
| 41 | +//! preventing crash bugs. Because of that, inherited mutability is |
| 42 | +//! preferred, and interior mutability is something of a last |
| 43 | +//! resort. Since cell types enable mutation where it would otherwise |
| 44 | +//! be disallowed though, there are occassions when interior |
| 45 | +//! mutability might be appropriate, or even *must* be used, e.g. |
| 46 | +//! |
| 47 | +//! * Introducing inherited mutability roots to shared types. |
| 48 | +//! * Implementation details of logically-immutable methods. |
| 49 | +//! * Mutating implementations of `clone`. |
| 50 | +//! |
| 51 | +//! ## Introducing inherited mutability roots to shared types |
| 52 | +//! |
| 53 | +//! Shared smart pointer types, including `Rc` and `Arc`, provide |
| 54 | +//! containers that can be cloned and shared between multiple parties. |
| 55 | +//! Because the contained values may be multiply-aliased, they can |
| 56 | +//! only be borrowed as shared references, not mutable references. |
| 57 | +//! Without cells it would be impossible to mutate data inside of |
| 58 | +//! shared boxes at all! |
| 59 | +//! |
| 60 | +//! It's very common then to put a `RefCell` inside shared pointer |
| 61 | +//! types to reintroduce mutability: |
| 62 | +//! |
| 63 | +//! ``` |
| 64 | +//! extern crate collections; |
| 65 | +//! |
| 66 | +//! use collections::HashMap; |
| 67 | +//! use std::cell::RefCell; |
| 68 | +//! use std::rc::Rc; |
| 69 | +//! |
| 70 | +//! fn main() { |
| 71 | +//! let shared_map: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::new())); |
| 72 | +//! shared_map.borrow_mut().insert("africa", 92388); |
| 73 | +//! shared_map.borrow_mut().insert("kyoto", 11837); |
| 74 | +//! shared_map.borrow_mut().insert("piccadilly", 11826); |
| 75 | +//! shared_map.borrow_mut().insert("marbles", 38); |
| 76 | +//! } |
| 77 | +//! ``` |
| 78 | +//! |
| 79 | +//! ## Implementation details of logically-immutable methods |
| 80 | +//! |
| 81 | +//! Occasionally it may be desirable not to expose in an API that |
| 82 | +//! there is mutation happening "under the hood". This may be because |
| 83 | +//! logically the operation is immutable, but e.g. caching forces the |
| 84 | +//! implementation to perform mutation; or because you must employ |
| 85 | +//! mutation to implement a trait method that was originally defined |
| 86 | +//! to take `&self`. |
| 87 | +//! |
| 88 | +//! ``` |
| 89 | +//! extern crate collections; |
| 90 | +//! |
| 91 | +//! use collections::HashMap; |
| 92 | +//! use std::cell::RefCell; |
| 93 | +//! |
| 94 | +//! struct Graph { |
| 95 | +//! edges: HashMap<uint, uint>, |
| 96 | +//! span_tree_cache: RefCell<Option<Vec<(uint, uint)>>> |
| 97 | +//! } |
| 98 | +//! |
| 99 | +//! impl Graph { |
| 100 | +//! fn minimum_spanning_tree(&self) -> Vec<(uint, uint)> { |
| 101 | +//! // Create a new scope to contain the lifetime of the |
| 102 | +//! // dynamic borrow |
| 103 | +//! { |
| 104 | +//! // Take a reference to the inside of cache cell |
| 105 | +//! let mut cache = self.span_tree_cache.borrow_mut(); |
| 106 | +//! if cache.is_some() { |
| 107 | +//! return cache.get_ref().clone(); |
| 108 | +//! } |
| 109 | +//! |
| 110 | +//! let span_tree = self.calc_span_tree(); |
| 111 | +//! *cache = Some(span_tree); |
| 112 | +//! } |
| 113 | +//! |
| 114 | +//! // Recursive call to return the just-cached value. |
| 115 | +//! // Note that if we had not let the previous borrow |
| 116 | +//! // of the cache fall out of scope then the subsequent |
| 117 | +//! // recursive borrow would cause a dynamic task failure. |
| 118 | +//! // This is the major hazard of using `RefCell`. |
| 119 | +//! self.minimum_spanning_tree() |
| 120 | +//! } |
| 121 | +//! # fn calc_span_tree(&self) -> Vec<(uint, uint)> { vec![] } |
| 122 | +//! } |
| 123 | +//! # fn main() { } |
| 124 | +//! ``` |
| 125 | +//! |
| 126 | +//! ## Mutating implementations of `clone` |
| 127 | +//! |
| 128 | +//! This is simply a special - but common - case of the previous: |
| 129 | +//! hiding mutability for operations that appear to be immutable. |
| 130 | +//! The `clone` method is expected to not change the source value, and |
| 131 | +//! is declared to take `&self`, not `&mut self`. Therefore any |
| 132 | +//! mutation that happens in the `clone` method must use cell |
| 133 | +//! types. For example, `Rc` maintains its reference counts within a |
| 134 | +//! `Cell`. |
| 135 | +//! |
| 136 | +//! ``` |
| 137 | +//! use std::cell::Cell; |
| 138 | +//! |
| 139 | +//! struct Rc<T> { |
| 140 | +//! ptr: *mut RcBox<T> |
| 141 | +//! } |
| 142 | +//! |
| 143 | +//! struct RcBox<T> { |
| 144 | +//! value: T, |
| 145 | +//! refcount: Cell<uint> |
| 146 | +//! } |
| 147 | +//! |
| 148 | +//! impl<T> Clone for Rc<T> { |
| 149 | +//! fn clone(&self) -> Rc<T> { |
| 150 | +//! unsafe { |
| 151 | +//! (*self.ptr).refcount.set((*self.ptr).refcount.get() + 1); |
| 152 | +//! Rc { ptr: self.ptr } |
| 153 | +//! } |
| 154 | +//! } |
| 155 | +//! } |
| 156 | +//! ``` |
| 157 | +//! |
| 158 | +// FIXME: Explain difference between Cell and RefCell |
| 159 | +// FIXME: Downsides to interior mutability |
| 160 | +// FIXME: Can't be shared between threads. Dynamic borrows |
| 161 | +// FIXME: Relationship to Atomic types and RWLock |
12 | 162 |
|
13 | 163 | use clone::Clone;
|
14 | 164 | use cmp::Eq;
|
|
0 commit comments