问题
I'm trying to understand Rust pointer types and their relation to mutability. Specifically, the ways of declaring a variable which holds the pointer and is itself mutable -- i.e. can be pointed to some other memory, and declaring that the data itself is mutable -- i.e. can be changed through the value of the pointer variable.
This is how I understand plain references work:
let mut a = &5; // a is a mutable pointer to immutable data
let b = &mut 5; // b is an immutable pointer to mutable data
So a
can be changed to point to something else, while b
can't. However, the data to which b
points to can be changed through b
, while it can't through a
. Do I understand this correctly?
For the second part of the question -- why does Box::new
seem to behave differently? This is my current understanding:
let mut a = Box::new(5); // a is a mutable pointer to mutable data
let c = Box::new(7); // c is an immutable pointer to immutable data
new
should return a pointer to some heap-allocated data, but the data it points to seems to inherit mutability from the variable which holds the pointer, unlike in the example with references where these two states of mutability are independent! Is that how Box::new
is supposed to work? If so, how can I create a pointer value to mutable data on the heap that is stored in an immutable variable?
回答1:
First, you do understand how references behave correctly. mut a
is a mutable variable (or, more correctly, a mutable binding), while &mut 5
is a mutable reference pointing to a mutable piece of data (which is implicitly allocated on the stack for you).
Second, Box
behaves differently from references because it is fundamentally different from references. Another name for Box
is owning/owned pointer. Each Box
owns the data it holds, and it does so uniquely, therefore mutability of this data is inherited from mutability of the box itself. So yes, this is exactly how Box
should work.
Another, probably more practical, way to understand it is to consider Box<T>
exactly equivalent to just T
, except of fixed size and allocation method. In other words, Box
provides value semantics: it is moved around just like any value and its mutability depends on the binding it is stored in.
There are several ways to create a pointer to a mutable piece of data on the heap while keeping the pointer immutable. The most generic one is RefCell
:
use std::cell::RefCell;
struct X { id: u32 }
let x: Box<RefCell<X>> = Box::new(RefCell::new(X { id: 0 }));
x.borrow_mut().id = 1;
Alternatively, you can use Cell
(for Copy
types):
let x: Box<Cell<u32>> = Box::new(Cell::new(0));
x.set(1);
Note that the above examples are using so-called "internal mutability" which should better be avoided unless you do need it for something. If you want to create a Box
with mutable interior only to keep mutability properties, you really shouldn't. It isn't idiomatic and will only result in a syntactic and semantic burden.
You can find a lot of useful information here:
- Ownership
- References and borrowing
- Mutability
- std::cell - internal mutability types
In fact, if you have a question about such fundamental things as mutability, it is probably already explained in the book :)
来源:https://stackoverflow.com/questions/31567708/difference-in-mutability-between-reference-and-box