Skip to content
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

remove pub syntax for struct/union fields #2059

Closed
andrewrk opened this issue Mar 13, 2019 · 12 comments · Fixed by #3498
Closed

remove pub syntax for struct/union fields #2059

andrewrk opened this issue Mar 13, 2019 · 12 comments · Fixed by #3498
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@andrewrk
Copy link
Member

Right now Zig lets you put pub in front of a struct/union field and it does nothing. Originally I was planning on this doing something, but now I'm not sure that private struct fields should be a thing in Zig.

@andrewrk andrewrk added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Mar 13, 2019
@andrewrk andrewrk added this to the 0.5.0 milestone Mar 13, 2019
@emekoi
Copy link
Contributor

emekoi commented Mar 14, 2019

is there any particular reason that you think that private fields shouldn't be a thing in zig?

@hryx
Copy link
Contributor

hryx commented Apr 6, 2019

I think this is a pretty important issue, and I'm somewhat torn. Here are the gnarliest bullet points for me.

Yay

Today, a struct initialization requires all fields to be set explicitly, either to a value or to undefined. This is fantastic: you can't forget a field, and if someone quietly breaks the API to a library you use, then your existing code will alert you with a compile error.

If some fields are public and some private, how will struct initialization work? It would be a big fat shame to lose the above benefit.

Nay

The change would mean there is no such thing as a private field. A pub fn of a struct communicates its API, but so do fields. So removing private fields means either:

  1. you are signalling to the user that all fields are meant to be read/written directly; or
  2. to uphold a properly encapsulated interface, your struct cannot have private state.

No. 1 violates "communicate intent precisely", and no. 2 is downright infeasible.

I wonder if there is some way to address this disadvantage with a yet-unexplored language feature.

@emekoi
Copy link
Contributor

emekoi commented Jun 22, 2019

now that we have default field values are there any other reasons pub should be removed for fields?

@distractedlambda
Copy link

I'm actually partial to the idea of keeping everything public in Zig, but also establishing a firm naming convention for "internal" symbols (e.g. prefix with an _). If a symbol name is formatted as an internal symbol name, you assume that it's not a part of the stable API.

Obviously this does not outright stop someone from accessing private symbols and watching their program break when a dependency updates, but it clearly communicates the intent of the API author, as much as any other solution to this problem.

My experience with languages that formalize access control, is that they often can't express the kinds of "access groupings" I want for the API I'm writing. It's pretty often that you end up with a group of types that all have good reason to know the internal details of each other, but which export an API that shouldn't require any access to internal details from outside that group. Languages have tried "friend" declarations or "file private"/"module private" symbols to capture these kinds of patterns, and I've found those solutions awkward at best. You end up organizing your code around the language's access control system, instead of around the logical separation between components.

Maybe more importantly, I'm not sure how Zig would incorporate a good formalized access control system with its current model of package organization. The compiler has no semantic understanding of a hierarchical relationship between files.

@ghost
Copy link

ghost commented Jul 15, 2019

@lucascharlesmyers

I'm actually partial to the idea of keeping everything public in Zig, but also establishing a firm naming convention for "internal" symbols (e.g. prefix with an _). If a symbol name is formatted as an internal symbol name, you assume that it's not a part of the stable API.

I have also thought about that, and maybe a better way to provide encapsulation is through interfaces anyway? That is, no encapsulation within a project, but when importing a 3rd party project or library, you only see what the "provided interfaces" of the library expose. In summary, encapsulation would be a package manager responsibility, not a language responsibility. Of course, naming conventions can still be used like you say.

@distractedlambda
Copy link

@user00e00
Swift uses this a lot (in addition to public and private and fileprivate, there's an internal access level that is accessible by everything in the same module). Things get dicey in that language because access control is intertwined with ABI stability and linking, but as long as Zig considers extern fn the only way to get a stable ABI, I think having "package-private" symbols would be reasonably ergonomic.

I think there are certain times where it's pragmatic to be able to access the internals of an external package you're using (e.g. working around a bug / an API deficiency), but I can see reasonable counter-arguments to that. Namely, if you allow somebody to work around deficiencies in an external library they're using, you might be making it less likely that the problem actually gets fixed upstream. If you don't allow messing with the private symbols of external dependencies, you at the very least force people to fork dependencies they want to alter, and forks are much more likely to turn into pull requests than workarounds at usage sites.

@bb010g
Copy link

bb010g commented Jul 20, 2019

How's this work with @import returning normal structs?

@distractedlambda
Copy link

@bb010g
If I understand what you're asking, I would think it would be sufficient for @import to "prune" structs according to some chosen rules. e.g. @importing a file gives you the full struct, private members and all, but @importing a package excludes private members from the returned struct. It might be prudent to then give those usage cases and semantics different names for clarity, e.g. @import and @importPackage, but that's a separate discussion.

@distractedlambda
Copy link

Also, thinking about it more, I'm strongly in favor of indicating access level with identifier names, i.e. have it baked into the language grammar that names following a particular pattern are to be treated as private/internal identifiers, in place of access-control keywords. Here's my reasoning:

  1. You're not always in a situation where you can "Go To Definition" on a symbol name you're unfamiliar with, and in those cases being able to know something about a symbol by its name is valuable. Requiring e.g. a special prefix on all private symbols, like _symbol, has very little cost when writing code, and can improve readability.
  2. It's been my experience that even in languages with access control keywords, patterns develop for naming "internal" or "implementation" symbols to avoid name conflicts with public symbols of what would otherwise be the same name. These patterns vary from developer to developer and codebase to codebase, which can make it harder to read code you're not familiar with. By forcing a naming convention at the language level, and therefore preventing private symbols from ever having the same names as public ones, we eliminate this source of inconsistency.

@jakwings
Copy link

Is there a summary of the reasons for the removal? What is the recommended way to mark fields that are subject to change without warnings?

@tmccombs
Copy link

tmccombs commented Jun 7, 2021

In C it is a pretty common pattern to declare a struct without any fields in the .h file so that the struct is opaque to client code, and it has to use the api. How would you do that in zig?

@daurnimator
Copy link
Contributor

In C it is a pretty common pattern to declare a struct without any fields in the .h file so that the struct is opaque to client code, and it has to use the api. How would you do that in zig?

https://ziglang.org/documentation/master/#opaque

However your question is off topic for this issue; consider asking in one of the community discussion areas: https://github.com/ziglang/zig/wiki/Community

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants