-
-
Notifications
You must be signed in to change notification settings - Fork 2.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
Require mutable variables outside scope to be qualified #8012
Comments
How would the following code qualify the access to x? var x: i32 = 0;
const S = struct {
fn increment() void {
x += 1;
}
}; Just testing the limits of the syntax, so pardon the contrived example. |
Good question: const GlobalScope = @This();
var x: i32 = 0;
const S = struct {
fn increment() void {
GlobalScope.x += 1;
}
}; |
I'm still split on whether this is a good idea for vars, but the above example pretty much rules out doing the same for consts, IMO. That would make global imports, definitions, etc. a huge pain to work with from within struct methods. |
If you included all those as well you actually wouldn't be able to reference anything outside your immediate scope:) Since type/imports are immutable there's no issue, the issue only applies when your variables can mutate. |
After thinking about it some more, I'm mostly against this proposal. The issue it addresses is real, but also a beginner mistake. While it would be nice to prevent it, Zig's modules-are-structs thing automatically propagates the "fix" to all mutable global variables, making them more cumbersome to use. And while global mutable state should be used in moderation, I feel that deliberately discouraging it with more cumbersome syntax is taking things too far. Prefixing all global vars with Individually, none of these objections are deal-breakers, but taken together, they tip the balance against doing this, IMO. |
Agreed. I would also add that this mistake will be easier to make for those who work more in other languages since alot of languages use the same syntax for local variables and instance fields.
Agreed. However, in some ways I see this as an advantage because it discourages use of global mutable variables. But like you say, when you really need to do this it's going to look more ugly. I'm personally not sure whether this one goes in the Pro or Con column.
This is true but I'm not seeing why this is bad. Does this cause some sort of footgun? The worst case I can think of is the user gets a compile error for not qualifying their mutable global state even though that's what they really want to do, the best case being that it wasn't their intention and a nasty bug was just caught by the compiler. Inconsistency isn't inherently good or bad, even though it's commonly use to describe things that are bad. For example, Zig now requires "arrayish" types be formatted with a specifier because the user's intention is ambiguous, but it doesn't require one for integer types. This is inconsistent but I would say its a good inconsistency that makes the library "easier to use correctly". I believe the same argument applies here. To be clear, I'm on the fence about this one. This particular footgun isn't a problem for me because I write alot of Zig code, but I can empathize with others who don't have as much experience with Zig. Ironically, it's likely the newer users who will have the most relevant perspective on this. |
No footgun -- it's just one more rule to learn about the language. Once you are familiar with it, it shouldn't matter much. But as a beginner you would probably stumble over it once or twice. |
Here's an interesting proposal I thought worth consideration. Require mutable variables outside the "immediate scope" to be accessed with their containing type as a qualifier. For example:
Why?
The footgun here is confusing "namespace variables" with "instance variables".
Zig veterans will immediately see the problem here;
z
is meant to be an instance variable but is mistakenly declared as a namespace variable. The poor soul who wrote this code has a long debug session in their future.Proposal #2859 was also meant to address this issue but took a different route. It proposed fixing the problem at the definition site by marking instance fields with the
field
keyword. That proposal was rejected but the idea of alleviating this confusion still has merit. This proposal tackles the problem at the "access" site. With this proposal, rather than a runtime bug, the example above would produce a compile-error:Now the user has a choice to either qualify
z
withS.z
orself.z
, explicitly stating whetherz
is part of the namespace or a field ofS
.One other thing to note is that I don't think accessing mutable variables outside the current scope is very common. So requiring qualification for this means that having to use a qualifier is more of a corner case and making it more verbose can actually be an advantage.
Also note that this proposal only restricts access to mutable variables. This is because this footgun is less likely with
const
variables becauseconst
variables more clearly not instance variables and also cannot be mutated directly (although indirect mutation is possible).Variation
Require qualification on both mutable and
const
variables. This one would be a much bigger change, however, some kinds ofconst
variables are still susceptible to the footgun because they can mutate internally. If we wanted to go this route, we could also consider whether Zig could make a distinction betweenconst
andmutable
variables recursively. So the proposal could be modified to say that a variable with any mutability must be qualified.The text was updated successfully, but these errors were encountered: