I'm experimenting with UnsafeCell and here is an example:
use std::cell::UnsafeCell;
fn get_ref_compile_error(cell: &UnsafeCell<i32>) -> &mut i32 {
&mut unsafe { *cell.get() } // error: cannot return reference to a temporary value
}
fn get_ref_compiles_fine(cell: &UnsafeCell<i32>) -> &mut i32 {
unsafe { &mut *cell.get() } // ok
}
fn main(){
let cell = UnsafeCell::new(1);
let ref_compiles_fine = unsafe { &mut *cell.get() }; // ok
let ref_compiles_fine_2 = &mut unsafe { *cell.get() }; // ok
}
I suspect the reason &mut unsafe { *cell.get() } does not compile is that the unsafe { *cell.get() } probably has the lifetime of the function block scope.
This seems a bit weird to me since borrowing the same object results in different behavior based on if the reference inside unsafe block or not.
QUESTION: Why is it ok to return unsafe { &mut *cell.get() }, but not &mut unsafe { *cell.get() } while this is in fact borrowing of the same object.
Here's a simpler example that shows the difference a little better:
The difference concerns place expressions and value expressions.
*sis a place expression, and when you apply&to this place expression, you get a reference value. However, blocks{}can only contain value expressions, so it tries to convert the place expression into a value by copying. Only after copying does it try to make a reference to the new value.Note: I swapped the order of these errors
The first error is because
Stringvalues can't be copied. Your code doesn't produce this error becausei32can be copied, but that's not what you want. You want to reference the existing value, not a reference to a copy.From there, the second error is more clear: (assuming the copy succeeds,) you're trying to create a reference to a value that was just created. When the function returns, that value would be dropped, and the reference would no longer be valid.
Here's an example where both versions work, and the difference you get as a result. The first one does what you expect, while the second leaves
xunchanged. This is what happens in your two// oklines at the end ofmain.In this code, the second
yis a reference to a value that undergoes temporary lifetime extension. That is, it's equivalent to this code:In this code, it should be clear that
ycan only be valid while_yexists, and that_yis separate fromx, which has been copied.Relating back to your code, unsafe blocks are a type of block, so they can only wrap value expressions. There's no way to wrap an unsafe place expression by itself, but since place expressions are typically very short, it's easy to wrap the surrounding value expression instead. And really,
&mut *is a single operation that casts a raw pointer into a reference.