On Saturday, 19 November 2022 at 09:26:49 UTC, Andrey Zherikov wrote:
> On Saturday, 19 November 2022 at 09:12:26 UTC, thebluepandabear wrote:
> That's the point many people have given here which is not convincing him, even though it is quite great.
I think we all know the answer here 😂
IMHO you are both right :)
You are speaking about API compatibility, []() {}()
speaks about ABI compatibility. The latter makes sense in long-running production software. So we know that there are no issues in API compatibility but we don't know anything about ABI.
Yes, this is a good summary. Redefining a raw field as a getter/setter pair keeps the API but breaks the ABI, so in some cases conservative getters/setters can be justified.
Another problem with raw fields:
struct S{int field;}
@safe clientCode()
{ auto s = new S();
doSomething(&s.field);
}
This will break if S is rewritten as
struct S
{ int field_;
auto field(){return field_;}
auto field(int val){return field_ = val;}
}
I though at first that breakage could still be avoided by instead writing
struct S
{ int field_;
ref field(){return field_;}
}
...but I tested and actually it does not work because in this case &s.field
takes the address of the getter function, not the field.
Now, despite all this I don't think it'd be a good idea to write everything to getters/setters just in case. The binary interface issue is frankly secondary: most structs and classes are just compiled along with their client code. And even separately compiled libraries only have to do this if they strive to provide a stable ABI between releases, which is not nearly always the case. Nor needs to be.
But still the pointer problem in the above example stands, so conservative getters/setters are justified in an API that's used widely enough. But probably not for something internal to a ten thousand line long package.