Repository navigation
Consider deprecation of UB-happy static mut #53639
Description
static mut is almost impossible to use correctly, see rust-lang-nursery/lazy-static.rs#117 for an example in the widely-used lazy-static.
You must be able to show that every borrow of the static mut is not reentrant (as opposed to regular interior mutability, which only requires reentrance-freedom when accessing data), which is almost entirely impossible in real-world scenarios.
We have a chance at removing it from Rust2018 and force people to use a proper synchronization abstraction (e.g. lazy_static! + Mutex), or in lieu of one, thread_local! / scoped_thread_local!.
If they were using static mut with custom synchronization logic, they should do this:
pub struct CustomSynchronizingAbstraction<T> {
/* UnsafeCell / Cell / RefCell / etc. around data, e.g. `T` */
}
// Promise that proper synchronization exists *around accesses*.
unsafe impl<T: Sync> Sync for CustomSynchronizingAbstraction<T> {}And then use CustomSynchronizingAbstraction with regular statics, safely.
This matches the "soundness boundary" of Rust APIs, whereas static mut is more like C.
cc @RalfJung @rust-lang/compiler @rust-lang/lang
2023-08 Note from triage, many years later: there's now https://doc.rust-lang.org/1.71.0/std/cell/struct.SyncUnsafeCell.html in the library, added by #95438, which can be used instead of static mut. But static mut is not even soft-deprecated currently.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsRejected/Not lang
Activity
Centril commentedon Aug 23, 2018
I don't know at all whether we should do this or not atm. But to make this decision I have an...
...Idea: We should do a crater run to scrutinize how many legitimate usages there are and how many ones there are that have UB.
nikomatsakis commentedon Aug 23, 2018
cc @rust-lang/wg-unsafe-code-guidelines -- we have this group just for this sort of thing =)
alercah commentedon Aug 23, 2018
I'm 100% behind getting rid of
static mut. Any attempt to use it which isn't UB is replaceable with something else which is safe, likely aMutexor something thread local. Or, per the original comment, a custom alternate type implementingSyncdirectly.joshtriplett commentedon Aug 23, 2018
We should clearly document the undefined behavior, but I don't think we should remove the ability to have (unsafe) global mutable locations directly, without any layer of indirection. That would make it more difficult to build certain types of lock-free synchronization, for instance.
eddyb commentedon Aug 23, 2018
@joshtriplett But you can always use
unsafe impl Sync+static?alercah commentedon Aug 23, 2018
Can you give an example of what you mean? You can't do much more with
static mutthan you could with a staticUnsafeCell.RalfJung commentedon Aug 23, 2018
Yeah, and that's just as unsafe. So TBH I do not see the point.
eddyb commentedon Aug 23, 2018
@RalfJung No, it's not, and I've explained why. With
unsafe impl Sync, you only have to prove the data accesses correct, but withstatic mut, the references themselves can conflict.EDIT: Now if "references exist" cannot be UB, ever, then this is not a problem, but IIUC, they do.
Also, you can't use a
static mutfrom safe code, only astatic. If thestatic mutis used correctly, why spread unsafety when you can wrap it in a sound API.alercah commentedon Aug 23, 2018
It's not actually different from an
UnsafeCellif we replace each&varwithunsafe {&*var.get()}, right? Then the aliasing rules would be the same; the difference is that with theUnsafeCellyou can still keep references to the cell itself?RalfJung commentedon Aug 23, 2018
@eddyb Ah okay I see -- that has nothing to with with
unsafe impltough and everything with&UnsafeCell.I agree
&UnsafeCellis safer thanstatic mut.eddyb commentedon Aug 23, 2018
@RalfJung Okay I should make it clearer that the
unsafe implis for "custom synchronization abstraction". I'll go edit the issue description.RalfJung commentedon Aug 23, 2018
If we could weaken
static mutto only give you raw pointers, that could help...... tough people would probably still turn them into references ASAP.
japaric commentedon Aug 23, 2018
How would they instantiate their custom type in stable Rust? User
cons fns are unstable and even if we do stabilizemin_const_fnthat doesn't include anything that has bounds so even with that your example won't compile on stable.To me this sounds like it would reduce what you can do in the 2018 edition. In the 2015 edition you can create
static mutvariables that contain primitive types but in the 2018 edition you can not do the equivalent unless I'm missing something like adding aRacyCelltype withconstconstructor tocore. (Here I'm assuming that it's not certain whethermin_const_fnwill make it into the edition release).228 remaining items
rfcbot commentedon Oct 16, 2024
🔔 This is now entering its final comment period, as per the review above. 🔔
traviscross commentedon Oct 16, 2024
@rustbot labels -I-lang-nominated
We discussed this in the 2024-10-16 lang triage call, and it's now in FCP (disposition close).
tmandry commentedon Oct 24, 2024
Overall I think I'm aligned with @RalfJung on the idea that for features requiring unsafe, we should default to trusting our users when we aren't creating surprise and when any alternative come with undue ergonomic burdens. That said, there was one thing that I didn't find myself aligned with..
I mostly agree with the first sentence, though I would observe that even those people who "know what they are doing" are subject to lapses in judgment when coding in anger. I disagree with the second. I think having "speed bumps" like writing
SyncUnsafeCellcan be an effective way of forcing someone to slow down and consider the implications of what they are doing. Overall I don't think we are ready to commit to that story to the point of deprecatingstatic mutfor reasons I'll explain in a follow-up comment, but I still see value in it.While it's true that there is a class of users that will mindlessly copy from an online resource, it is ultimately their right to consider the risk profile of what they are doing and override warning signs along the way. There's nothing wrong if someone trying to get Rust running on a development board commits heinous safety violations in their impassioned campaign for blinky LEDs. They have a simple enough problem and hopefully enough expertise that they can reason about the rules they are bending, and we are not primarily designing for that user in that moment. Neither are we designing for the user who is ultimately reckless and hasn't yet learned that the indiscriminate use of
unsafecan cost them dearly later on.We are designing for the user who in the next moment takes time to consider what crimes they want to
commit(in their repository) and the user who is, for one reason or another, willing to use unsafe but a bit more risk averse than the first.tmandry commentedon Oct 24, 2024
Okay, I've skimmed this thread. I think it's compelling to have a future without
static mutand where there is a clear replacement.SyncUnsafeCellmight be that replacement, though it is a bit fiddly compared to the ergonomics ofstatic muttoday.Given how we got here, I am comfortable for now with the current story that
static mutgives you a*mutand that the same caveats that apply to any*mutapply here – with the caveat that it is probably less forgiving of temporary lapses in judgment, since it's so easily accessed from multiple threads and from different places in your code. We are still in a much better place today than we were; with the lint, we have removed almost any possibility of surprising UB.Closing the gap that remains
The remaining "gap" is about weighing the cost in ergonomics for those who know what they are doing, plus the cost of a transition, against the risk of accidental unsound code. I can see this being a difficult gap to close. On one side we could improve ergonomics of
SyncUnsafeCell(or some equivalent), though I don't see any proposals on the table that seem ready to be accepted, and/or do an automated transition across an edition boundary. On the other side we could collect data that says enough people still write unsound code that this is a problem (or that they don't, but that is difficult with any of the ways we collect such data today; absence of evidence isn't evidence of absence).The default outcome is that we never close the gap. In the absence of data that this is a real-world issue (even though I suspect it will be), that default outcome is probably correct.
Making a judgment call
In the absence of data, applying judgment here is difficult in that it balances two highly sensitive topics – the reliability of code in Rust, even
unsafecode, and the ergonomics of unsafe code, aka "getting things done". Both, in my view, are critical for Rust's success.At a higher level, the data have says that Rust is far more reliable than just about any alternative, but less ergonomic than its alternatives when it comes to low-level systems tasks. I suspect this is in the back of the minds of other lang team members checking their boxes. Given my opinion that we should lean toward making design decisions on the basis of code rather than abstract properties of a language, this is useful data to base a decision on. Coupled with the cost of making a transition, defaulting to the status quo seems like the right course of action for now.
Conclusion
I agree that clippy may want to lint aggressively against
static mut. I also agree that we might want to revisit this question in a future with better mechanisms for decomposing safety guarantees. For now though, my impression is that we have solved the most important problem we've observed in the ecosystem, and we should wait for more data or more progress in other areas before going further.Effectively this checkbox is a vote for postponement, but the distinction isn't important enough for me to restart the FCP now.
@rfcbot reviewed
RalfJung commentedon Oct 24, 2024
Note that there is one aspect of
static mutthat doesn't currently have any alternative. It's somewhat of an accidental feature I think (I wasn't around when this got permitted), and it's unclear whether and how we'd best provide a "nicer" alternative:This is an instance of lifetime extension, which we generally reject for
&mutin the outermost scope of astatic/const(where it "extends" things to'staticlifetime) -- except instatic mutwhere we do accept this, and let it implicitly create a global mutable allocation holding the[0, 1, 2].I'm not happy that we accept this, but we don't have any alternative we can migrate existing users to either -- except for the super verbose one:
rfcbot commentedon Oct 26, 2024
The final comment period, with a disposition to close, as per the review above, is now complete.
As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.
ehuss commentedon Jan 13, 2025
Something that came up was some concerns about educating people on what to do if they hit the
static_mut_refslint (tracked in #123059). I have opened rust-lang/edition-guide#354 with a first draft with some recommendations. I would appreciate if anyone could look it over and provide suggestions for things to fix/change/add (or if anything seemed unclear).