Yes, you are assigning to a temporary, because X implement Copy, so doing { x } you are creating a block that return x, but X implement Copy, so your block return a copy of x. Your block is basicly this:
#[derive(Copy, Clone)]
pub struct X {
pub a: u8,
}
fn main() {
let x = X { a: 0 };
let mut tmp = { x };
tmp.a = 5;
assert_eq(x.a, 0);
assert_eq!(tmp.a, 5);
}
What you can do to make the block return the actual variable instead of the copy is to transform the pointer to a mutable reference, and return that ref from the unsafe block:
#[derive(Copy, Clone)]
pub struct X {
pub a: u8,
}
fn main() {
let mut x = X { a: 0 };
let ptr: *mut X = &mut x;
unsafe {
&mut *ptr
}.a = 5;
assert_eq!(x.a, 5);
}
Why is it allowed? It is allowed because Rust lets you modify temporaries, because a lot of things would be very verbose otherwise, like:
fn get_first<T>(vec: Vec<T>) -> Option<T> {
let first = vec.into_iter().next();
// ^ temporary iterator
first
}
next() needs a mutable ref to the iterator. Having to bind it to a mutable variable would mean that you would have to write:
fn get_first<T>(vec: Vec<T>) -> Option<T> {
let mut iter = vec.into_iter();
let first = iter.next();
first
}
The snippets of forbidden code that you provided don't satisfy the compiler because you try to assign a value to another value, not to a variable: doing { var } creates a block that return the value in the variable, so:
({ x.a }) = 5;
// ^ block return the value in x.a
let mut y: u8 = 0;
({ y }) = 5;
// ^ same here, return the value in y
You can consume this temporary, but not overwrite it, this is intentional and a good thing.
I think in C++ they call this lvalue and rvalue, the block return a rvalue, and you can't assign to a rvalue.
But yes the compiler don't see { x }.a as a temporary, which is a bit silly, there is no use case that I can think of, and can create confusion.