|
1 | 1 | % Mutability
|
2 | 2 |
|
3 |
| -Coming Soon |
| 3 | +Mutability, the ability to change something, works a bit differently in Rust |
| 4 | +than in other languages. The first aspect of mutability is its non-default |
| 5 | +status: |
| 6 | + |
| 7 | +```rust,ignore |
| 8 | +let x = 5; |
| 9 | +x = 6; // error! |
| 10 | +``` |
| 11 | + |
| 12 | +We can introduce mutability with the `mut` keyword: |
| 13 | + |
| 14 | +```rust |
| 15 | +let mut x = 5; |
| 16 | + |
| 17 | +x = 6; // no problem! |
| 18 | +``` |
| 19 | + |
| 20 | +This is a mutable [variable binding][vb]. When a binding is mutable, it means |
| 21 | +you’re allowed to change what the binding points to. So in the above example, |
| 22 | +it’s not so much that the value at `x` is changing, but that the binding |
| 23 | +changed from one `i32` to another. |
| 24 | + |
| 25 | +[vb]: variable-bindings.html |
| 26 | + |
| 27 | +If you want to change what the binding points to, you’ll need a [mutable reference][mr]: |
| 28 | + |
| 29 | +```rust |
| 30 | +let mut x = 5; |
| 31 | +let y = &mut x; |
| 32 | +``` |
| 33 | + |
| 34 | +[mr]: references-and-borrowing.html |
| 35 | + |
| 36 | +`y` is an immutable binding to a mutable reference, which means that you can’t |
| 37 | +bind `y` to something else (`y = &mut z`), but you can mutate the thing that’s |
| 38 | +bound to `y`. (`*y = 5`) A subtle distinction. |
| 39 | + |
| 40 | +Of course, if you need both: |
| 41 | + |
| 42 | +```rust |
| 43 | +let mut x = 5; |
| 44 | +let mut y = &mut x; |
| 45 | +``` |
| 46 | + |
| 47 | +Now `y` can be bound to another value, and the value it’s referencing can be |
| 48 | +changed. |
| 49 | + |
| 50 | +It’s important to note that `mut` is part of a [pattern][pattern], so you |
| 51 | +can do things like this: |
| 52 | + |
| 53 | +```rust |
| 54 | +let (mut x, y) = (5, 6); |
| 55 | + |
| 56 | +fn foo(mut x: i32) { |
| 57 | +# } |
| 58 | +``` |
| 59 | + |
| 60 | +[pattern]: patterns.html |
| 61 | + |
| 62 | +# Interior vs. Exterior Mutability |
| 63 | + |
| 64 | +However, when we say something is ‘immutable’ in Rust, that doesn’t mean that |
| 65 | +it’s not able to be changed: We mean something has ‘exterior mutability’. Consider, |
| 66 | +for example, [`Arc<T>`][arc]: |
| 67 | + |
| 68 | +```rust |
| 69 | +use std::sync::Arc; |
| 70 | + |
| 71 | +let x = Arc::new(5); |
| 72 | +let y = x.clone(); |
| 73 | +``` |
| 74 | + |
| 75 | +[arc]: ../std/sync/struct.Arc.html |
| 76 | + |
| 77 | +When we call `clone()`, the `Arc<T>` needs to update the reference count. Yet |
| 78 | +we’ve not used any `mut`s here, `x` is an immutable binding, and we didn’t take |
| 79 | +`&mut 5` or anything. So what gives? |
| 80 | + |
| 81 | +To this, we have to go back to the core of Rust’s guiding philosophy, memory |
| 82 | +safety, and the mechanism by which Rust guarantees it, the |
| 83 | +[ownership][ownership] system, and more specifically, [borrowing][borrowing]: |
| 84 | + |
| 85 | +> You may have one or the other of these two kinds of borrows, but not both at |
| 86 | +> the same time: |
| 87 | +> |
| 88 | +> * 0 to N references (`&T`) to a resource. |
| 89 | +> * exactly one mutable reference (`&mut T`) |
| 90 | +
|
| 91 | +[ownership]: ownership.html |
| 92 | +[borrowing]: borrowing.html#The-Rules |
| 93 | + |
| 94 | +So, that’s the real definition of ‘immutability’: is this safe to have two |
| 95 | +pointers to? In `Arc<T>`’s case, yes: the mutation is entirely contained inside |
| 96 | +the structure itself. It’s not user facing. For this reason, it hands out `&T` |
| 97 | +with `clone()`. If it handed out `&mut T`s, though, that would be a problem. |
| 98 | + |
| 99 | +Other types, like the ones in the [`std::cell`][stdcell] module, have the |
| 100 | +opposite: interior mutability. For example: |
| 101 | + |
| 102 | +```rust |
| 103 | +use std::cell::RefCell; |
| 104 | + |
| 105 | +let x = RefCell::new(42); |
| 106 | + |
| 107 | +let y = x.borrow_mut(); |
| 108 | +``` |
| 109 | + |
| 110 | +[stdcell]: ../std/cell/index.html |
| 111 | + |
| 112 | +RefCell hands out `&mut` references to what’s inside of it with the |
| 113 | +`borrow_mut()` method. Isn’t that dangerous? What if we do: |
| 114 | + |
| 115 | +```rust,ignore |
| 116 | +use std::cell::RefCell; |
| 117 | +
|
| 118 | +let x = RefCell::new(42); |
| 119 | +
|
| 120 | +let y = x.borrow_mut(); |
| 121 | +let z = x.borrow_mut(); |
| 122 | +# (y, z); |
| 123 | +``` |
| 124 | + |
| 125 | +This will in fact panic, at runtime. This is what `RefCell` does: it enforces |
| 126 | +Rust’s borrowing rules at runtime, and `panic!`s if they’re violated. This |
| 127 | +allows us to get around another aspect of Rust’s mutability rules. Let’s talk |
| 128 | +about it first. |
| 129 | + |
| 130 | +## Field-level mutability |
| 131 | + |
| 132 | +Mutabilty is a property of either a borrow (`&mut`) or a binding (`let mut`). |
| 133 | +This means that, for example, you cannot have a [`struct`][struct] with |
| 134 | +some fields mutable and some immutable: |
| 135 | + |
| 136 | +```rust,ignore |
| 137 | +struct Point { |
| 138 | + x: i32, |
| 139 | + mut y: i32, // nope |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +The mutability of a struct is in its binding: |
| 144 | + |
| 145 | +```rust,ignore |
| 146 | +struct Point { |
| 147 | + x: i32, |
| 148 | + y: i32, |
| 149 | +} |
| 150 | +
|
| 151 | +let mut a = Point { x: 5, y: 6 }; |
| 152 | +
|
| 153 | +a.x = 10; |
| 154 | +
|
| 155 | +let b = Point { x: 5, y: 6}; |
| 156 | +
|
| 157 | +b.x = 10; // error: cannot assign to immutable field `b.x` |
| 158 | +``` |
| 159 | + |
| 160 | +[struct]: structs.html |
| 161 | + |
| 162 | +However, by using `Cell<T>`, you can emulate field-level mutability: |
| 163 | + |
| 164 | +``` |
| 165 | +use std::cell::Cell; |
| 166 | +
|
| 167 | +struct Point { |
| 168 | + x: i32, |
| 169 | + y: Cell<i32>, |
| 170 | +} |
| 171 | +
|
| 172 | +let mut point = Point { x: 5, y: Cell::new(6) }; |
| 173 | +
|
| 174 | +point.y.set(7); |
| 175 | +
|
| 176 | +println!("y: {:?}", point.y); |
| 177 | +``` |
| 178 | + |
| 179 | +This will print `y: Cell { value: 7 }`. We’ve successfully updated `y`. |
0 commit comments