This is because `my_var` actually denotes a place in memory, and there's multiple things one can do with a place:
one can load the contents of the place from memory (which produces a value), one can create a pointer to the place (which also produces a value, but does not access memory at all),
or one can store a value into this place (which in Rust produces the `()` value, but the side-effect of changing the contents of memory is more relevant).
-Besides local variables, the other main example of a place expression is the result of the `*` operator, which takes a *value* (of pointer type) and turns it into a place.
+Besides local variables, the other main example of a place expression is the result of the `*` operator, which takes a *value* (of pointer type) and turns it into a place.[^deref]
Furthermore, given a place of struct type, we can use a field projection to obtain a place just for that field.
+[^deref]: **Update (2025-12-26):** If you check the [Rust reference](https://doc.rust-lang.org/reference/expressions.html#r-expr.place-value.place-context), you may notice that it actually says that `*` takes a *place* expression. This is a somewhat peculiar Rust design choice related to custom smart pointers implementing the `Deref` trait, and related to borrow checking. That turns out to be easier if you only dereference places. However, when it comes to the operational semantics, the overall picture is much cleaner if we say that `*` works on values.
+
This may sound odd, because it means that `let new_var = my_var;` is not actually a valid statement in our grammar!
To accept this code, the Rust compiler will automatically convert this statement into a form that fits the grammar by adding `load` whenever needed.[^desugar]
`load` takes a place and, as the name indicates, performs a load from memory to obtain the value currently stored in this place.
The scrutinee of a `match` expression is a place expression, and if the pattern is `_` then a value is never constructed.
However, when an actual binder is present, this introduces a local variable and a place-to-value coercion is inserted to compute the value that will be stored in that local variable.
+**Note on `unsafe` blocks.**
+Note that wrapping an expression in a block forces it to be a value expression.
+This means that `unsafe { *ptr }` always loads from the pointer!
+In other words:
+```rust
+let ptr = std::ptr::null::<i32>();
+let _ = *ptr; // This is fine!
+let _ = unsafe { *ptr }; // This is UB.
+```
+The fact that braces force a value expression can occasionally be useful, but the fact that `unsafe` blocks do that is definitely quite unfortunate.
+
### Are there also value-to-place coercions?
So far, we have discussed what happens when a place expression is encountered in a spot where a value expression was expected.