This question already has an answer here:
What are non-lexical lifetimes?
(1 answer)
Closed 12 months ago.
I understand why Rust doesn't allow us to do the following:
let v1 = vec![1,2,3];
let v2 = v1;
println!("Print here {}", v1[0])
But now, let's look at this, which is allowed and correct.
let v1 = vec![1,2,3];
let v2 = &v1; // borrowing here
println!("Print here {}", v1[0])
Now, I am really curious why 2nd one is allowed. For the first example, on the stack, some information gets stored(pointer of the memory address of the actual vector data, length of the vector). let v2 = v1 causes pointer and length of data to be copied from that part of the stack and gets stored on the stack again, but now, for v2 variable. Since changing v1 causes vector data to be changed + the part of the stack where v1's information was stored, rust doesn't allow this, because v2 now becomes unpredictable as it still stores the old length so stack and heap doesn't agree.
For the 2nd example, I am wondering what let v2 = &v1 does exactly so that this is allowed? I'd appreciate the explanation in the same words as I described the first case above.
When reasoning about what Rust allows or does not allow, in term of ownership, it's not useful to think about what happens exactly in memory, because that's not how Rust thinks about it. It has a more high-level approach.
The fundamental issue with the first code is that you have emptied the variable v1 from having any value, when assigning its value to v2. That is, each value (part of memory) in Rust must have an owner (that is, a variable that has a lifetime, and which is responsible for that value), and only one owner.
When you move v1's value to v2, nothing happens in memory (it' most likely a NOP with sufficient optimization levels), but in the Great Scheme of Rust, the value's owner has changed. But then, to ensure there is only one owner, Rust makes it like v1's value moved. It's a way to tell you: it's just like there if there was nothing any more at the address of this variable, stop using it until you have put something else yourself, because I won't allow you to access what is really there until then. And since v1 was declared non-mutable anyway, this variable is pretty much useless from now on.
In the second case, the situation is completely different. The ownership of the underlying value is still v1's. v2 has a different type of access to the value: it is not responsible for it, has no write-access, and must give it back before the value is freed from memory (that is, before v1 is droped). However, for the lifetime of v2 (that is, for as much time as v2 could be used somewhere), the value is "locked" (this has nothing to do with threading locks), in the sense that you are not allowed to write to it, to share it with write-access or to give its ownership to somebody else. If you wanted to share the data with write-access, you don't want to give the ownership (presumably because you want to reuse the variable after the borrow has ended, and getting the ownership back is verbose), then you can give a mutable borrow. These are exclusive (meaning at most one other person can have a mutable borrow) and prevent the actual owner from reading or writing while the borrow is on-going.
It would be achieved like this:
let mut owner = vec![1, 2, 3];
let borrower = &mut owner;
println!("Print here {}", owner[0]);
However, if you compiled this code, you'll see it works. Strange, isn't it? I just said even the owner has no read-access to the value... The key is that the lifetime of the borrow ends just before the owner tries to read-access it, that is, Rust understands that, when trying to access the Vec, borrower will never use its borrow any more, so it can be dropped just before the access. The following code will not compile, because in this case it cannot drop borrower before owner tried to access the borrowed value.
let mut owner = vec![1, 2, 3];
let borrower = &mut owner;
println!("Print here: {}", owner[0]);
println!("Print there: {}", borrower[0]);
Related
I'm new to Rust and already have read "the book" but I'm trying to understand the inners of references in the language.
As far as I know a reference is a type of pointer that takes you to some value in memory when dereferenced.
let x = 5
let y = &x
In this example y is a pointer to the memory address of x and so the type and value of y is not equal to the type and value of x.
assert_eq!(y, x)
// fails to compile as assert_eq! has no implementation for `&{integer} == {integer}`
Then a reference is not the same as the value it references to.
But if I dereference the y by using the * operator I now do get the value it referenced and so the following code compiles.
assert_eq!(*y, x)
To access the value the reference points to, dereferencing is needed; but dereferencing implies moving the ownership of the referenced value to the new variable.
let x = Point {x:1, y:2};
let y = &x;
let z = *y;
// fails to compile as move occurs because `*y` has type `Point`, which does not implement the `Copy` trait
By implementing the Copy trait for the type (Point in this case) the problem can be solved by letting Rust create a copy of the value and move the ownership of the copied value to the new variable.
The final question is, how can Rust access a value of a type that does not implement the Copy or Clone traits and is behind a reference without having the dereference (*) function take ownership of the value, thus making other shared references to the original value invalid?
E.g. (works just fine)
let x = Point {x:1, y:2};
let y = &x;
let z = &x;
fn print_point(a: &Point){
println!("{a:#?}")
}
println!("Printing y");
print_point(y);
println!("Printing x");
println!("{x:#?}");
println!("Printing z");
print_point(z);
(Playground)
The ownership semantics of Rust are more complex than simply giving ownership or denying access.
First, there is a little background to be explained about pointers in Rust. A pointer is just an integer that indicates an index of the memory. It's usually beginner-friendly to think of it as an arrow pointing to a location in memory. An arrow is not what it points to, but it's easy to get the value pointed by an arrow given the arrow (just look where it points to).
All pointer-like in Rust are either just that arrow, or that arrow a little more information about that value (so that you don't have to read the value to know that information). By pointer-like I mean anything that behaves like a pointer, that is, can be dereferenced (to keep it simple).
For example, the borrow of x (&x) is not exactly a pointer ("actual" pointers in Rust are used less often than borrows and would be either *mut x or *const x), it's a pointer-like. This pointer-like is just an integer: once your program is compilated, you couldn't tell the difference between a borrow and a pointer. However, contrary to "just a number", a borrow holds some additional constraints. For instance, it's never a null-pointer (that is, a pointer that points to the very-first position of the memory; by convention — and for technical reasons concerning the allocator — this area is never allocated, that is, this pointer is always invalid). In fact, more generally, a borrow should never be invalid: it should always be safe to dereference it (NB. safe doesn't mean legal, Rust could prevent you from dereferencing it, as in the example you posted), in the sense that the value that it points to always "makes sense". This is still not the strongest guarantee provided by a borrow, which is that, for as long as someone holds the borrow (that is, for as long as someone could use it), no one is going to modify the value it points to. This is not something you have to be careful about: Rust will prevent you from writing code that could break this.
Similarly, a mutable borrow (&mut x) is also a pointer-like that is "just an integer", but has different constraints. A mutable borrow (also called "exclusive borrow"), besides always being valid, ensures that, as long as one person is holding it, no one else can access the value it points to, either to modify it or just to read it. This is to prevent data races, because the one that hold the exclusive borrow can modify the value, even though they don't own the value.
These are definitively the most common pointer-like used in Rust (they're everywhere), and could be seen as the read-only but multi-access version of a pointer, or the read-and-write but single-access version of a pointer.
Having understood that, it's easier to understand Rust's logic. If I have y = &x, I am allowed to read the value x, but I can't "take" it (that is, take its ownership): I can't even write to it (but I should be able to if I owned it)! Note that even if I have an exclusive borrow, I couldn't take the ownership of x, but I could "swap" it: take its ownership in exchange for the ownership of a variable I owned (or create an owned variable for the occasion). For this reason, if you write let z = *y, you are taking the ownership of x so Rust complains. But note that this is due to the binding, not the the dereferencing of y. To prove it, compare it with the following (String is not Copy):
let a = String::new();
let b = &a;
let c = &a;
assert_eq(*b, *c);
playground
Here, I dereference the borrow to a non-Copy value, but it's clear that I am not violating the borrow contract (to compare equality, I just need to read the value).
Furthermore, in general, if I have a borrow of a struct, I can obtain borrows of the its fields.
Incidentally, note that String is a pointer-like too! It's not "just a number" (meaning it carries more information than just being an arrow). These pointer-like that are more than "just a number" are called fat pointers. String is a fat pointer to an owned str, that is, String is a kind of pointer that also ensure that whomever owns it also owns the pointed value, which is why String is not Copy (otherwise, the pointed value could be owned by multiple parties).
To access the value the reference points to, dereferencing is needed; but dereferencing implies moving the ownership of the referenced value to the new variable.
Dereferencing does not imply moving. The motivating example presented let z = *y simply dereferences y and then moves/copies the value to z. Yet if you do not assign the value to a new variable there is no new variable and therfore no transfer of ownership.
This might help building an intuition:
You make the (correct) case that a reference is a different thing from the object it points to. However implying that you would need to assign it after dereferencing in order to do anything with it (wrong). Most of the time you are fine working directly on the members, which may be copyable. If not maybe the members of the members are. In the end it is likely to all just to boil down to bytes.
When would you be required to use Cell or RefCell? It seems like there are many other type choices that would be suitable in place of these, and the documentation warns that using RefCell is a bit of a "last resort".
Is using these types a "code smell"? Can anyone show an example where using these types makes more sense than using another type, such as Rc or even Box?
It is not entirely correct to ask when Cell or RefCell should be used over Box and Rc because these types solve different problems. Indeed, more often than not RefCell is used together with Rc in order to provide mutability with shared ownership. So yes, use cases for Cell and RefCell are entirely dependent on the mutability requirements in your code.
Interior and exterior mutability are very nicely explained in the official Rust book, in the designated chapter on mutability. External mutability is very closely tied to the ownership model, and mostly when we say that something is mutable or immutable we mean exactly the external mutability. Another name for external mutability is inherited mutability, which probably explains the concept more clearly: this kind of mutability is defined by the owner of the data and inherited to everything you can reach from the owner. For example, if your variable of a structural type is mutable, so are all fields of the structure in the variable:
struct Point { x: u32, y: u32 }
// the variable is mutable...
let mut p = Point { x: 10, y: 20 };
// ...and so are fields reachable through this variable
p.x = 11;
p.y = 22;
let q = Point { x: 10, y: 20 };
q.x = 33; // compilation error
Inherited mutability also defines which kinds of references you can get out of the value:
{
let px: &u32 = &p.x; // okay
}
{
let py: &mut u32 = &mut p.x; // okay, because p is mut
}
{
let qx: &u32 = &q.x; // okay
}
{
let qy: &mut u32 = &mut q.y; // compilation error since q is not mut
}
Sometimes, however, inherited mutability is not enough. The canonical example is reference-counted pointer, called Rc in Rust. The following code is entirely valid:
{
let x1: Rc<u32> = Rc::new(1);
let x2: Rc<u32> = x1.clone(); // create another reference to the same data
let x3: Rc<u32> = x2.clone(); // even another
} // here all references are destroyed and the memory they were pointing at is deallocated
At the first glance it is not clear how mutability is related to this, but recall that reference-counted pointers are called so because they contain an internal reference counter which is modified when a reference is duplicated (clone() in Rust) and destroyed (goes out of scope in Rust). Hence Rc has to modify itself even though it is stored inside a non-mut variable.
This is achieved via internal mutability. There are special types in the standard library, the most basic of them being UnsafeCell, which allow one to work around the rules of external mutability and mutate something even if it is stored (transitively) in a non-mut variable.
Another way to say that something has internal mutability is that this something can be modified through a &-reference - that is, if you have a value of type &T and you can modify the state of T which it points at, then T has internal mutability.
For example, Cell can contain Copy data and it can be mutated even if it is stored in non-mut location:
let c: Cell<u32> = Cell::new(1);
c.set(2);
assert_eq!(c.get(), 2);
RefCell can contain non-Copy data and it can give you &mut pointers to its contained value, and absence of aliasing is checked at runtime. This is all explained in detail on their documentation pages.
As it turned out, in overwhelming number of situations you can easily go with external mutability only. Most of existing high-level code in Rust is written that way. Sometimes, however, internal mutability is unavoidable or makes the code much clearer. One example, Rc implementation, is already described above. Another one is when you need shared mutable ownership (that is, you need to access and modify the same value from different parts of your code) - this is usually achieved via Rc<RefCell<T>>, because it can't be done with references alone. Even another example is Arc<Mutex<T>>, Mutex being another type for internal mutability which is also safe to use across threads.
So, as you can see, Cell and RefCell are not replacements for Rc or Box; they solve the task of providing you mutability somewhere where it is not allowed by default. You can write your code without using them at all; and if you get into a situation when you would need them, you will know it.
Cells and RefCells are not code smell; the only reason whey they are described as "last resort" is that they move the task of checking mutability and aliasing rules from the compiler to the runtime code, as in case with RefCell: you can't have two &muts pointing to the same data at the same time, this is statically enforced by the compiler, but with RefCells you can ask the same RefCell to give you as much &muts as you like - except that if you do it more than once it will panic at you, enforcing aliasing rules at runtime. Panics are arguably worse than compilation errors because you can only find errors causing them at runtime rather than at compilation time. Sometimes, however, the static analyzer in the compiler is too restrictive, and you indeed do need to "work around" it.
No, Cell and RefCell aren't "code smells". Normally, mutability is inherited, that is you can mutate a field or a part of a data structure if and only if you have exclusive access to of the whole data structure, and hence you can opt into mutability at that level with mut (i.e., foo.x inherits its mutability or lack thereof from foo). This is a very powerful pattern and should be used whenever it works well (which is surprisingly often). But it's not expressive enough for all code everywhere.
Box and Rc have nothing to do with this. Like almost all other types, they respect inherited mutability: you can mutate the contents of a Box if you have exclusive, mutable access to the Box (because that means you have exclusive access to the contents, too). Conversely, you can never get a &mut to the contents of an Rc because by its nature Rc is shared (i.e. there can be multiple Rcs referring to the same data).
One common case of Cell or RefCell is that you need to share mutable data between several places. Having two &mut references to the same data is normally not allowed (and for good reason!). However, sometimes you need it, and the cell types enable doing it safely.
This could be done via the common combination of Rc<RefCell<T>>, which allows the data to stick around for as long as anyone uses it and allows everyone (but only one at a time!) to mutate it. Or it could be as simple as &Cell<i32> (even if the cell is wrapped in a more meaningful type). The latter is also commonly used for internal, private, mutable state like reference counts.
The documentation actually has several examples of where you'd use Cell or RefCell. A good example is actually Rc itself. When creating a new Rc, the reference count must be increased, but the reference count is shared between all Rcs, so, by inherited mutability, this couldn't possibly work. Rc practically has to use a Cell.
A good guideline is to try writing as much code as possible without cell types, but using them when it hurts too much without them. In some cases, there is a good solution without cells, and, with experience, you'll be able to find those when you previously missed them, but there will always be things that just aren't possible without them.
Suppose you want or need to create some object of the type of your choice and dump it into an Rc.
let x = Rc::new(5i32);
Now, you can easily create another Rc that points to the exact same object and therefore memory location:
let y = x.clone();
let yval: i32 = *y;
Since in Rust you may never have a mutable reference to a memory location to which any other reference exists, these Rc containers can never be modified again.
So, what if you wanted to be able to modify those objects and have multiple Rc pointing to one and the same object?
This is the issue that Cell and RefCell solve. The solution is called "interior mutability", and it means that Rust's aliasing rules are enforced at runtime instead of compile-time.
Back to our original example:
let x = Rc::new(RefCell::new(5i32));
let y = x.clone();
To get a mutable reference to your type, you use borrow_mut on the RefCell.
let yval = x.borrow_mut();
*yval = 45;
In case you already borrowed the value your Rcs point to either mutably or non-mutably, the borrow_mut function will panic, and therefore enforce Rust's aliasing rules.
Rc<RefCell<T>> is just one example for RefCell, there are many other legitimate uses. But the documentation is right. If there is another way, use it, because the compiler cannot help you reason about RefCells.
So I just started learning rust (first few chapters of "the book") and am obviously quite a noob. I finished the ownership-basics chapter (4) and wrote some test programs to make sure I understood everything. I seem to have the basics down but I asked myself why immutable references to copy-types are even possible. I will try to explain my thoughts with examples.
I thought that you maybe want to store a reference to a copy-type so you can check it's value later instead of having a copy of the old value but this can't be it since the underlying value can't be changed as long as it's been borrowed.
The most basic example of this would be this code:
let mut x = 10; // push i32
let x_ref = &x; // push immutable reference to x
// x = 100; change x which is disallowed since it's borrowed currently
println!("{}", x_ref); // do something with the reference since you want the current value of x
The only reason for this I can currently think of (with my current knowledge) is that they just exist so you can call generic methods which require references (like cmp) with them.
This code demonstrates this:
let x = 10; // push i32
// let ordering = 10.cmp(x); try to compare it but you can't since cmp wants a reference
let ordering = 10.cmp(&x) // this works since it's now a reference
So, is that the only reason you can create immutable references to copy-types?
Disclaimer:
I don't see Just continue reading the book as a valid answer. However I fully understand if you say something like Yes you need those for this and this use-case (optional example), it will be covered in chapter X. I hope you understand what I mean :)
EDIT:
Maybe worth mentioning, I'm a C# programmer and not new to programming itself.
EDIT 2:
I don't know if this is technically a duplicate of this question but I do not fully understand the question and the answer so I hope for a more simple answer understandable by a real noob.
An immutable reference to a Copy-type is still "an immutable reference". The code that gets passed the reference can't change the original value. It can make a (hopefully) trivial copy of that value, but it can still only ever change that copy after doing so.
That is, the original owner of the value is ensured that - while receivers of the reference may decide to make a copy and change that - the state of whatever is referenced can't ever change. If the receiver wants to change the value, it can feel free; nobody else is going to see it, though.
Immutable references to primitives are not different, and while being Copy everywhere, you are probably more inclined to what "an immutable reference" means semantically for primitive types. For instance
fn print_the_age(age: &i32) { ... }
That function could make a copy via *age and change it. But the caller will not see that change and it does not make much sense to do so in the first place.
Update due to comment: There is no advantage per se, at least as far as primitives are concerned (larger types may be costly to copy). It does boil down to the semantic relationship between the owner of the i32 and the receiver: "Here is a reference, it is guaranteed to not change while you have that reference, I - the owner - can't change or move or deallocate and there is no other thread else including myself that could possibly do that".
Consider where the reference is coming from: If you receive an &i32, wherever it is coming from can't change and can't deallocate. The `i32´ may be part of a larger type, which - due to handing out a reference - can't move, change or get de-allocated; the receiver is guaranteed of that. It's hard to say there is an advantage per se in here; it might be advantageous to communicate more detailed type (and lifetime!) relationships this way.
They're very useful, because they can be passed to generic functions that expect a reference:
fn map_vec<T, U>(v: &Vec<T>, f: impl Fn(&T) -> U) -> Vec<U> {...}
If immutable references of non-Copy types were forbidden, we would need two versions:
fn map_vec_own<T: !Copy, U>(v: &Vec<T>, f: impl Fn(&T) -> U) -> Vec<U> {...}
fn map_vec_copy<T: Copy, U>(v: &Vec<T>, f: impl Fn( T) -> U) -> Vec<U> {...}
Immutable references are, naturally, used to provide access to the referenced data. For instance, you could have loaded a dictionary and have multiple threads reading from it at the same time, each using their own immutable reference. Because the references are immutable those threads will not corrupt that common data.
Using only mutable references, you can't be sure of that so you need to make full copies. Copying data takes time and space, which are always limited. The primary question for performance tends to be if your data fits in CPU cache.
I'm guessing you were thinking of "copy" types as ones that fit in the same space as the reference itself, i.e. sizeof(type) <= sizeof(type*). Rust's Copy trait indicates data that could be safely copied, no matter the size. These are orthogonal concepts; for instance, a pointer might not be safely copied without adjusting a refernce count, or an array might be copyable but take gigabytes of memory. This is why Rc<T> has the Clone trait, not Copy.
How would you mutate a vector in such a way where you would need an immutable reference to said vector to determine how you would need to mutate the vector? For example, I have a piece of code that looks something like this, and I want to duplicate the last element of the vector:
let mut vec: Vec<usize> = vec![123, 42, 10];
// Doesn't work of course:
vec.push(*vec.last().unwrap())
// Works, but is this necessary?
let x = *vec.last().unwrap();
vec.push(x);
immutable reference [...] to determine how you would need to mutate the vector?
The short answer is you don't. Any mutation to the vector could possibly invalidate all existing references, making any future operations access invalid data, potentially causing segfaults. Safe Rust doesn't allow for that possibility.
Your second example creates a copy of the value in the vector, so it no longer matters what happens to the vector; that value will continue to be valid.
What's unfortunate about the first example is that if you follow the order of operations, a human can tell that the immutable value is retrieved before the mutation happens. In fact, that's why the multiple-statement version is possible at all! This is indeed a current limitation of the Rust borrow checker. There is investigation ongoing to see if some of these types of limitations can be lifted.
When would you be required to use Cell or RefCell? It seems like there are many other type choices that would be suitable in place of these, and the documentation warns that using RefCell is a bit of a "last resort".
Is using these types a "code smell"? Can anyone show an example where using these types makes more sense than using another type, such as Rc or even Box?
It is not entirely correct to ask when Cell or RefCell should be used over Box and Rc because these types solve different problems. Indeed, more often than not RefCell is used together with Rc in order to provide mutability with shared ownership. So yes, use cases for Cell and RefCell are entirely dependent on the mutability requirements in your code.
Interior and exterior mutability are very nicely explained in the official Rust book, in the designated chapter on mutability. External mutability is very closely tied to the ownership model, and mostly when we say that something is mutable or immutable we mean exactly the external mutability. Another name for external mutability is inherited mutability, which probably explains the concept more clearly: this kind of mutability is defined by the owner of the data and inherited to everything you can reach from the owner. For example, if your variable of a structural type is mutable, so are all fields of the structure in the variable:
struct Point { x: u32, y: u32 }
// the variable is mutable...
let mut p = Point { x: 10, y: 20 };
// ...and so are fields reachable through this variable
p.x = 11;
p.y = 22;
let q = Point { x: 10, y: 20 };
q.x = 33; // compilation error
Inherited mutability also defines which kinds of references you can get out of the value:
{
let px: &u32 = &p.x; // okay
}
{
let py: &mut u32 = &mut p.x; // okay, because p is mut
}
{
let qx: &u32 = &q.x; // okay
}
{
let qy: &mut u32 = &mut q.y; // compilation error since q is not mut
}
Sometimes, however, inherited mutability is not enough. The canonical example is reference-counted pointer, called Rc in Rust. The following code is entirely valid:
{
let x1: Rc<u32> = Rc::new(1);
let x2: Rc<u32> = x1.clone(); // create another reference to the same data
let x3: Rc<u32> = x2.clone(); // even another
} // here all references are destroyed and the memory they were pointing at is deallocated
At the first glance it is not clear how mutability is related to this, but recall that reference-counted pointers are called so because they contain an internal reference counter which is modified when a reference is duplicated (clone() in Rust) and destroyed (goes out of scope in Rust). Hence Rc has to modify itself even though it is stored inside a non-mut variable.
This is achieved via internal mutability. There are special types in the standard library, the most basic of them being UnsafeCell, which allow one to work around the rules of external mutability and mutate something even if it is stored (transitively) in a non-mut variable.
Another way to say that something has internal mutability is that this something can be modified through a &-reference - that is, if you have a value of type &T and you can modify the state of T which it points at, then T has internal mutability.
For example, Cell can contain Copy data and it can be mutated even if it is stored in non-mut location:
let c: Cell<u32> = Cell::new(1);
c.set(2);
assert_eq!(c.get(), 2);
RefCell can contain non-Copy data and it can give you &mut pointers to its contained value, and absence of aliasing is checked at runtime. This is all explained in detail on their documentation pages.
As it turned out, in overwhelming number of situations you can easily go with external mutability only. Most of existing high-level code in Rust is written that way. Sometimes, however, internal mutability is unavoidable or makes the code much clearer. One example, Rc implementation, is already described above. Another one is when you need shared mutable ownership (that is, you need to access and modify the same value from different parts of your code) - this is usually achieved via Rc<RefCell<T>>, because it can't be done with references alone. Even another example is Arc<Mutex<T>>, Mutex being another type for internal mutability which is also safe to use across threads.
So, as you can see, Cell and RefCell are not replacements for Rc or Box; they solve the task of providing you mutability somewhere where it is not allowed by default. You can write your code without using them at all; and if you get into a situation when you would need them, you will know it.
Cells and RefCells are not code smell; the only reason whey they are described as "last resort" is that they move the task of checking mutability and aliasing rules from the compiler to the runtime code, as in case with RefCell: you can't have two &muts pointing to the same data at the same time, this is statically enforced by the compiler, but with RefCells you can ask the same RefCell to give you as much &muts as you like - except that if you do it more than once it will panic at you, enforcing aliasing rules at runtime. Panics are arguably worse than compilation errors because you can only find errors causing them at runtime rather than at compilation time. Sometimes, however, the static analyzer in the compiler is too restrictive, and you indeed do need to "work around" it.
No, Cell and RefCell aren't "code smells". Normally, mutability is inherited, that is you can mutate a field or a part of a data structure if and only if you have exclusive access to of the whole data structure, and hence you can opt into mutability at that level with mut (i.e., foo.x inherits its mutability or lack thereof from foo). This is a very powerful pattern and should be used whenever it works well (which is surprisingly often). But it's not expressive enough for all code everywhere.
Box and Rc have nothing to do with this. Like almost all other types, they respect inherited mutability: you can mutate the contents of a Box if you have exclusive, mutable access to the Box (because that means you have exclusive access to the contents, too). Conversely, you can never get a &mut to the contents of an Rc because by its nature Rc is shared (i.e. there can be multiple Rcs referring to the same data).
One common case of Cell or RefCell is that you need to share mutable data between several places. Having two &mut references to the same data is normally not allowed (and for good reason!). However, sometimes you need it, and the cell types enable doing it safely.
This could be done via the common combination of Rc<RefCell<T>>, which allows the data to stick around for as long as anyone uses it and allows everyone (but only one at a time!) to mutate it. Or it could be as simple as &Cell<i32> (even if the cell is wrapped in a more meaningful type). The latter is also commonly used for internal, private, mutable state like reference counts.
The documentation actually has several examples of where you'd use Cell or RefCell. A good example is actually Rc itself. When creating a new Rc, the reference count must be increased, but the reference count is shared between all Rcs, so, by inherited mutability, this couldn't possibly work. Rc practically has to use a Cell.
A good guideline is to try writing as much code as possible without cell types, but using them when it hurts too much without them. In some cases, there is a good solution without cells, and, with experience, you'll be able to find those when you previously missed them, but there will always be things that just aren't possible without them.
Suppose you want or need to create some object of the type of your choice and dump it into an Rc.
let x = Rc::new(5i32);
Now, you can easily create another Rc that points to the exact same object and therefore memory location:
let y = x.clone();
let yval: i32 = *y;
Since in Rust you may never have a mutable reference to a memory location to which any other reference exists, these Rc containers can never be modified again.
So, what if you wanted to be able to modify those objects and have multiple Rc pointing to one and the same object?
This is the issue that Cell and RefCell solve. The solution is called "interior mutability", and it means that Rust's aliasing rules are enforced at runtime instead of compile-time.
Back to our original example:
let x = Rc::new(RefCell::new(5i32));
let y = x.clone();
To get a mutable reference to your type, you use borrow_mut on the RefCell.
let yval = x.borrow_mut();
*yval = 45;
In case you already borrowed the value your Rcs point to either mutably or non-mutably, the borrow_mut function will panic, and therefore enforce Rust's aliasing rules.
Rc<RefCell<T>> is just one example for RefCell, there are many other legitimate uses. But the documentation is right. If there is another way, use it, because the compiler cannot help you reason about RefCells.