-
-
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
Proposal: Make field access logically safe and in line with Zen #21544
Comments
This seems quite closely related to #9909, which was rejected. In the example for
|
Example using # as the symbol for internal fields is already possible. Just add it to variable name. const Example = struct {
// regular field
a: usize,
// internal field
_b: usize,
};
var example: Example = .{.a = 0, ._b = 0}; // compiles fine
var example2: Example = .{.a = 0, .b = 0}; // compile error - no such field in Example
example.a = 42; // compiles fine
example._b = 404; // compiles fine
example.b = 9001; // compile error - accessing non existant field directly |
Those are some excellent comments. While this proposal tackles some of the same issues as #9909, it is fundamentally different as it doesn't tries to hide or prevent access to memory that you as the caller own. My main issues with prefixing "internal fields" with _ today, is that it is an arbitrary convention not set by the language or the community as a whole. Same can be said about other mitigation strategies such as relying on comment, naming ( internal_field_a: usize,) and coding styles forbidding field writes (get/set methods). Arbitrary convention and coding styles that mitigate the issue, has historically been present as a way to mitigate null access. Considering your feedback I think a better solution would be to make the prefix _ have a special property on fields. const Example = struct {
// regular field
a: usize,
// internal field
_b: usize,
}; var example: Example = .{.a = 0, ._b = 0}; // compiles fine
var example2: Example = .{.a = 0, .b = 0}; // compile error - writing to internal field without _ prefix
example.a = 42; // compiles fine
example._b = 404; // compiles fine
example.b = 9001; // // compile error - writing to internal field without _ prefix
var read_b: usize = example.b // compiles fine
var read_b2: usize = example._b // compiles fine The std.ArrayList example, assuming len and items are declared in std using _ prefix. var array_list: std.ArrayList(usize) // init
var length: usize = array_list.items.len // compiles fine
var length2: usize = array_list._items._len // compiles fine
array_list._items._len = 5; // compiles fine
array_list.items.len = 5; // compile error - writing to internal field without _ prefix I will update my proposal to reflect these changes |
Please do not file a proposal to change the language |
Bakground
Field access (write) is logically unsafe and goes against Zen.
It is inconsistent with how other unsafe operations are handled (null & error).
Consider a struct whose fields are logically dependent on each other.
There is no way for the user of said struct to tell that the following field access of a is logically unsafe and causes a bug.
A silly, but concrete example would be to override the len property of std.ArrayList.
The bug would be free to "travel" and the actual failure point could be far away from the unsafe field access.
For instance the following loop works perfectly fine provided that he overridden .len is within .capacity.
Field access is also inconsistent with how Zig handles other unsafe accesses such as with Optionals.
"Nullable types" are marked and there are mechanisms in place to aid in safe usage.
I would argue that the need for safe field access is the same as that for safe nullable access.
Checking if a field is safe to write to and checking whether a value is set before using it can be argued to be the same kind of issue. Not having it reintroduces the same issues that have historically plagued null, but for field access (write).
The goal of the proposal is to provide a idiomatically way to express field intent that can catch accidental writes / misuse.
Proposed solution
Updated proposal
Add a special property for fields marked with _ allowing reads to be done without the prefix.
Introduce a common Zig convention using the _ prefix on fields to mark that the field has dependents and is not safe to arbitrarily change. This allows clear intent to be expressed with minimal clutter and accidental writes will be caught by the compiler.
The std.ArrayList example, assuming len and items are declared in std using _ prefix.
Zen
The proposed change would help communicate intent more precisely resulting in more readable code.
Furthermore it will move a class of bugs to compile time errors resulting in less error prone code.
Lastly one does not need to know / remember a struct in its entirety to use and access it safely.
The following Zen points would be improved with the proposed change:
Old proposal - kept as context for comments
Introduce an optional field keyword or symbol for marking a field as internal.
This marking provides no guarantees and the fields visibility and write ability remains unchanged.
What changes are compiler checks requiring that direct access is done trough an extra symbol similar to .? for nullable types.
Example using # as the new symbol.
The text was updated successfully, but these errors were encountered: