-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Allow safe access to some static mut
vars
#177
Conversation
👎 As described in great detail in the referenced issue, this is not even remotely safe. It's also a complete mis-use of the
The definition of The counterargument is that since a The general rule for Given this definition, since the mere ability to take a The only situation in which this RFC actually affects things is when the I believe that there are alternative approaches that we can use to solve the |
@kballard: do you have an alternative you prefer? I also think we should also consider putting |
While CTFE will allow for nicer initializers, I don't think it will help with requiring |
The safe functions in the |
|
||
Requiring unsafe access to these types unnecessarily introduces `unsafe` blocks | ||
in otherwise safe programs, and have the risk of leading to further unsafe | ||
behaviors if it's unclear what exactly in the block is unsafe. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has there been an RFC or discussion on other ways to achieve this? We only have three sources of unsafety, could we mark up the unsafe {}
with which we are intending on using?
unsafe<RawDeref + MutStatic + UnsafeFn> { }
Sorry it is a little off topic, I'm just not 100% across all the previous discussions and this seems like a similar solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has come up in the past before, although there haven't been any concrete proposals in the area, more of just ideas. For now though, the RFC is written with the assumption that this does not exist (but it would be quite nice!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, once rust-lang/rust#15701 is implemented it could be a lint, interpreting things like #[behaviour(raw_deref, mut_static)] unsafe { ... }
.
I would imagine that the large portion of safety that most Rust users consider is only safe code considered as a whole. Can you concretely explain how you believe this proposal is unsafe assuming there is no unsafe code? |
@alexcrichton: that's why I said "some" usages of Two thoughts off the top of my head without thinking about it too deeply:
|
I view the addition of a new kind of static to the language as more than it's worth given the problem that this RFC is trying to solve. The naming would also have to be quite different because otherwise we'll have a system where
This is certainly plausible! I personally believe that |
Is there a potential that the If initialization isn't a problem, I don't see why reading a Storing |
Thankfully no, all static variables are required to have initializers, and initializers are always just bit patterns so there are no statics which are ever uninitialized at any time.
The borrow checker cannot guarantee this because it doesn't have total knowledge, but this is why |
One issue here is because statics are used in 2 contexts here - there are static VALUES (which are, for example, useful in initializers) and static VARIABLES (assignables, by Harper's nomelecture) which have an identity and can potentially be modified. Static values are values. changing them makes as much sense as executing 1+1 = 3. Note that sometimes (e.g. mutex static initializers) they need to contain an Unsafe. Being values, they still can't be mutated. Static assignables are accessible through the program. It seems that a decent way of handling them is to make them behave like a &'static T parameter passed to all functions in the program. Of course given multithreading they need to be Share. |
Thinking about it more, the initializers-can't-be-priv problem may be annoying: If one wants a manually-synchronized static (i.e. a static variable which can't be proven synchronized by the type-system), then with my proposal it would need to be wrapped withing a Share wrapper. Because of the initializer-can't-be-priv issue, you can't have an externally-safe wrapper in a library. For example struct MyDSWrapper {
// intentionally private
inner: Unsafe<ManuallySynchronizedDS>
}
impl share for MyDSWrapper;
static mut svar: MyDSWrapper = MyDSWrapper
{ inner: ManuallySynchronizedDS { ... }};
pub fn work() {
let x = external_lock();
do_something_with(&mut unsafe { *svar.get() });
} However, an inline wrapper wouldn't be much more dangerous than most other uses of unsafe (i.e. uncareful safe code in the same module can trigger undefined behaviour). I don't think this is a big problem but some don't like it. However, this isn't particularly related to static muts but it is rather the way Rust handles unsafe. |
Rust takes the same approach with
If you take a look at |
But the unsafety is limited to Furthermore, I think there's a fundamental difference between arguing that access to a struct field can produce unsafety vs
Yes. Make The issue here is that this then allows e.g. |
You cannot assume there is no unsafe code. That's the problem. You, the library author, do not have control over the Maybe we should just get rid of |
Regarding safety, I am of the opinion that this proposal does not introduce any new risks for unsafe code. It is always true that unsafe operations may interfere with safe operations (that is, indeed, why we call them unsafe). The rules that govern static mut are that when one writes to a static mut, one must ensure that this read is ordered with respect to all reads of that static mut. As @kballard notes, these reads may occur in safe code or in unsafe code. In practice, this probably means that privacy must be used to restrict the set of code where the static mut can be accessed. This is par for the course with unsafe code. The fact that the writes must be ordered with respect to "safe" reads is not unusual and occurs in many situations. For example, transmuting a pointer can easily violate Rust's aliasing rules, which would render otherwise safe code unsafe. The public field in |
Well the unsafety problem is slightly different from the Vec problem - actually it is the already-present problem with Unsafe (when one can take the value while the Unsafe is borrowed by unsafe code) - but this problem has nothing to do with statics. From some point of view, it can be said that this is different from the Vec problem - because in the Vec problem the undefined behaviour happens within unsafe code, while here the problem is that safe code can see aliasing & and an &mut. I would prefer that this problem is fixed (mostly because it would make the spurious write semantics simpler) but it (still) has nothing to do with statics. |
That said, I think there is an argument for using Conceivably we could have three kinds of declarations:
A
And then having
Under this scheme, the The downside is that there are three things, but in a sense those three things exist today, it's just that we use Worth thinking about. |
What's the difference in your proposal between static and static mut? |
I like This could also be coupled with my earlier suggestion to remove The difference is |
In this case I'm not sure why |
Not sure what you mean by a |
By "Share wrapper" I meant a wrapper around Unsafe that is Share. And I think unsafe fields are orthogonal to public-initialize-private-access fields. (For example, a public-initialize-private-access field would be rather bad on Vec). |
@nikomatsakis Under the three-way distinction you propose, would (Premature bikeshedding: one small issue with the particular naming suggested here is that it would make IINM, it also wouldn't be possible to take an |
I think that it would be preferable to let only consts be "compile-time known" - you can always declare |
I think there's three big reasons why I view The first is that a The second is that the unsafety with Note here, again, that the only reason the This is why I very strongly favor the suggestions that allow for The third reason is that, with Ultimately, it just feels like library-provided invariants are things that are never expected to be checked by the compiler, whereas
The first one isn't important to this discussion, but the second is. Borrowck enforces it normally, so the only way to violate it is with The problem is, this RFC very explicitly breaks this otherwise-inviolate guarantee. It provides a mechanism to produce a |
Incidentally, I retract my suggestion to remove |
The Unsafe reference problem would still exist if we have By the way, could we talk on IRC? |
If
I don't think there would necessarily be a problem with allowing |
You can already write an My current viewpoint is:
|
@arielb1 Regarding
FWIW, with the If we can fix the |
@kballard I don't know why you say that a static mut is "expected" to be public. Indeed, I expect just the opposite. |
On Wed, Jul 23, 2014 at 09:08:55AM -0700, Gábor Lehel wrote:
No.
No.
Only consts.
Yes. |
Maybe that's the confusion here. Go look at |
On Thu, Jul 24, 2014 at 10:22:38AM -0700, Kevin Ballard wrote:
@kballard I guess I wasn't precise enough. I expect any static muts |
Ultimately, I still feel like this is a violation of I do get the argument that mutation is still unsafe and therefore the guarantee can be provided on the mutation side. But I just don't consider that persuasive. Generally, the approach Rust takes is to require Also, the more we talk about this, the more I'm convinced that requiring users to use |
6357402
to
e0acdf4
Compare
Closing in favor of #246, which is now accepted. |
Rendered