In Rust programming language, if a type T implements trait Deref, it can be used like a normal shared reference. By the same token, if a type T implements trait DerefMut, it can be used like a normal mutable reference. However, the following code may not behave what you expect.

use std::cell::{RefCell, RefMut};

fn main() {
    let x: RefCell<String> = RefCell::new(String::from("hello"));
    let y: RefMut<'_, String> = x.borrow_mut();
    y.push_str("world");
    println!("{y}");
}

Actually, this code does not compile!

$ cargo run
   Compiling foo v0.1.0 (/tmp/foo)
error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
 --> src/main.rs:6:5
  |
6 |     y.push_str("world");
  |     ^ cannot borrow as mutable
  |
help: consider changing this to be mutable
  |
5 |     let mut y = x.borrow_mut();
  |         +++

For more information about this error, try `rustc --explain E0596`.
error: could not compile `foo` (bin "foo") due to 1 previous error

Rust compiler complains that y is not mutable here. However, since RefMut implements DerefMut, we can use it like a &mut, can't we? Just like the following code

fn main() {
    let mut x: String = String::from("hello");
    let y: &mut String = &mut x;
    y.push_str("world");
    println!("{y}");
}

Unfortunately, this is not the case. For smart pointers like y: RefMut, if we write *y, what actually happens behind the hood is this:

*(y.deref_mut());

deref_mut() is the method defined by the trait DerefMut, its method signature looks like this:

fn deref_mut(&mut self) -> &mut T

Everything makes sense at this moment. In the code at the very beginning, the Rust compiler complained that y cannot be borrowed as mutable and suggested us adding the keyword mut in front of y. This makes sense because if y is immutable, then it cannot be borrowed as a mutable reference. After applying the fix suggested by the compiler, everything goes smoothly.

use std::cell::{RefCell, RefMut};

fn main() {
    let x: RefCell<String> = RefCell::new(String::from("hello"));
    let mut y: RefMut<'_, String> = x.borrow_mut();
    y.push_str("world");
    println!("{y}");
}

$ cargo run
   Compiling foo v0.1.0 (/tmp/foo)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
     Running `target/debug/foo`
helloworld

The lesson we learned here is that though smart pointers act like normal references, they are not references inherently. Be careful when interacting with them!