9 ms·
> Speaking of structure fields, they're always public. Structures and functions are private by default with an option to make them public. But struct fields can
by haberman 4y ago
> Speaking of structure fields, they're always public. Structures and functions are private by default with an option to make them public. But struct fields can only be public.
This feels like a mistake to me. I own and maintain a C library, and lack of private struct members is one of the things that causes the most problems. My users frequently reach into struct members even though I really do not want them to. This causes problems when those members have subtly different semantics than what they expect. It's even worse when I want to change the internal representation, and I have to track down all my users' code and change it accordingly.
In C, my only options are:
(1) Define the structure in a .c file instead of .h. This works great in cases where I can take the performance hit of not being able to inline accesses of those struct members. But for performance-critical structures where I need to be able to inline, this is not an option.
(2) Make the member names something like private_foo, internal_foo, etc. and hope my users take the hint. But this also makes my own code more ugly and creates longer lines that are more likely to wrap.
Unfortunately Zig is even less capable than C in this regard, because Zig takes away option (1). Since there is no header/source split in Zig, I cannot make a struct opaque by defining it in a source file only. Though I do see that there is "opaque {}", perhaps that plus some casting could accomplish a similar thing?
I see that rationale for not having private struct members was given here: https://github.com/ziglang/zig/issues/9909#issuecomment-942686366 https://github.com/ziglang/zig/issues/9909#issuecomment-9426...
> The idea of private fields and getter/setter methods was popularized by Java, but it is an anti-pattern. Fields are there; they exist. They are the data that underpins any abstraction. My recommendation is to name fields carefully and leave them as part of the public API, carefully documenting what they do. [...] In my subjective experience, public fields generally lead to better abstractions by eliminating the temptation to attempt full encapsulation, when the more effective strategy is to provide composable abstraction layers.
Java does indeed represent an anti-pattern of verbose and frequently trivial getters and setters. But newer languages like C#, Swift, and Dart have more elegant and low-overhead syntax for properties, even allowing a property to move between being an actual field member and being derived, without breaking users.
As a library maintainer, full encapsulation is very important for evolution of a system over time. The only way for layers to truly be layers is if the contract at each layer boundary is clear. Otherwise the layers gel together and you cannot safely change any one layer independently.
- michaelg7x 4y agoWhat are the downsides of keeping the public fields in a struct which is in turn embedded in a private struct created by the library? Inferring the "outer offset" could get irritating but it depends on your tolerance of additional complexity (in every public function...).
- haberman 4y ago> What are the downsides of keeping the public fields in a struct which is in turn embedded in a private struct created by the library? That seems similar to using "opaque {}", if I am understanding you correctly? I think either of those solutions could potentially work, at the cost of one explicit cast in each function that actually needs to access the data. I would love to know if this works well enough in practice.
- JonChesterfield 4y agoThe answer in C would be it constrains ordering/packing of fields, so you'd end up with N public/private wrappers and generally a lot of line noise, but I have a vague recollection that zig reorders fields as it sees fit anyway making that a non-issue
- hota_mazi 4y ago> Java does indeed represent an anti-pattern of verbose and frequently trivial getters and setters. It's not an anti-pattern. This is how Java provides the exact functionality that you describe as very important (field encapsulation). Because Java doesn't support properties, it needs to go through getters and setters. As you point out, more modern languages support this functionality directly in the language with properties.
- haberman 4y agoTo me the anti-pattern is lots of redundant, trivial, verbose getters and setters. If I see a source file with tens or hundreds of lines of trivial method definitions, I think something has gone wrong. Properties help avoid this anti-pattern by making it possible to transparently swap in a computed property for what used to be a plain field. This removes the incentive to proactively create trivial getters and setters "just in case."