Hello, why code like this is not allowed?
struct NotDefaultNotCopy;
fn main() {
let foo: &'static mut NotDefaultNotCopy = Box::leak(Box::new(NotDefaultNotCopy));
let foo = *foo;
}
Hello, why code like this is not allowed?
struct NotDefaultNotCopy;
fn main() {
let foo: &'static mut NotDefaultNotCopy = Box::leak(Box::new(NotDefaultNotCopy));
let foo = *foo;
}
No matter what you do to a reference, there must always be a valid value behind the reference at the end. If what you wrote was allowed, then you would end up with a valid value of type NotDefaultNotCopy
both in the second foo
variable, and in the leaked Box
. Since the value is not Copy
, this kind of duplication is not allowed.
Well, it's not allowed in general, for any lifetime 'life
in &' life T
.
One reason is that given only the borrow, the compiler has no idea where else the referent of type T
could be used. Moving out when it's used elsewhere would obviously not be great for reliability. This is a concern for any borrow, regardless of its lifetime.
If anything, the 'static
lifetime would make it more likely that it would be in use elsewhere in the process.
For a &'static mut T
exactly, you do know there are no other observers, modulo unsafe
. There could be a function to unsafely read and return the T
(and discard the &'static mut T
).
But if &'static mut T
is consumed, there is no reference anymore, and uninit bytes are never accessed again, in safe context, as this example is
For example, miri is happy with that code (but detects leak) playground
Here is code with more unsafe but with no miri leak warnings: playground
You could allow this operation with &'static mut T
. But the language simply doesn't. There's only one type in the Rust language that can be consumed by the dereference operator, and that's Box
. The &mut T
type doesn't allow it, and there's no special case in the language for &'static mut T
.
I mean, miri would be happy even if you actually duplicate the value. There's nothing unsound about that at the language level.
Would this duplicated value be ever accessible from safe code? If no, how is it different from move like let a = b
or as argument passing?
Your take
method is probably fine and will not actually allow duplication in practice. The Rust rules are designed to treat &'static mut T
like any other &mut T
, and that's why the language has no built-in way to do it.
As for duplication under miri, consider this example.
By the way, it may be related, in Lambda Rust (mathematical model of a subset of Rust) "owned pointer" represents full ownership of a value
RustBelt, section 3.3
Owned pointers ownn Ď„ are used to represent full ownership of (some part of) a heap allocation.
Because we model the stack using heap allocations, owned pointers also represent Rust’s local,
stack-allocated variables. As usual, Ď„ is the type of the pointee. Furthermore, n tracks the size of the
entire allocation. This can be different from the size of Ď„ for inner pointers that point into a larger
data structure.4 Still, most of the time, n is the size of Ď„ , in which case we omit the subscript.
I'd somehow missed the mut
in the middle there. With that in there, I agree.
Still, what should the semantics of such a deref be? How should that even be implemented, given that the design of Deref
and DerefMut
enforce that *
returns a &<SmartPtrType as Deref>::Target
?
And then there's the runtime semantics. What should happen to the place containing the deref'ed value after an "owning deref" has taken place?
It raises design questions, is all I'm saying. Ones that probably are answerable (e.g. by taking inspiration from Box<T>
asl @alice points out), but as of yet haven't been.
All the operator symbols are defined to be special cases for some built-in types, and use a trait for everything else. So &
, &mut
, and Box
don't go through Deref
because they're special cases. This also happens with the mathematical operators and numeric primitives.
The same thing that happens to memory when you move an owned value: nothing. It's statically guaranteed to not be used anymore.
This isn't ideal, since normally you want to free that memory when it comes from Box
, but it's the only thing that could happen. As a result, it's probably not very useful.
The general capability in terms of Box
has been discussed various times (search for DerefMove
and/or DerefPure
). But personally, I feel giving the capabilities to &'static mut T
directly seems too special-cased and surprising for the sake a niche need.
@Ddystopia if you're going to cross post, tell us about it.
But how do you even reliably consume the &'static mut
in the first place? One can create non-overlapping (hence valid) &'static mut _
references to the static item out of thin air, so this is 100% unsound and UB.
There is no static
item here; the &’static mut
in the example comes from Box::leak
, which is a safe function.
How do you express "this &'static mut
must come from a leaked Box" at the type system level, though? If you don't, and allow arbitrary statics to be passed to that function, the problem persists.
I guess this is a classic example of misusing/overusing references. If OP wants ownership of the value inside the Box (or the whole box), they should just pass the Box itself, instead of doing dirty tricks with unsafe and invalid references.
You don’t need to— &’static mut T
means that you have exclusive access to T
until the end of the program, regardless of how it was created. Among other things, that means that it’s impossible for any other code to observe any inconsistent state you might put T
into.
Any code that uses unsafe
to create an &’static mut T
where this isn’t true is, as you point out, necessarily unsound. But that’s tangential to the question at hand.
I’m not sure if this is true: Given &’something T
, you can generally put T
into whatever inconsistent state you want as long as it’s valid by the time ’something
ends. When ’something
== ’static
, that period where you can do inconsistent things extends until the end of program execution.
That's a contradiction, AFAIK - 'static
references to the same value are always overlapping, no matter how they are actually used.