-
Notifications
You must be signed in to change notification settings - Fork 205
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
Should structs be immutable? #2362
Comments
In general, my take on this is that WRT the semantic aspect, I don't like the idea of adding an indirection, and I think there is real value in having a variant of classes with structural identity. This leaves the idea of requiring identity to be preserved only for structs with mutable fields, which I think is ok, so long as there is no way to subclass/implement a struct and add in a mutable field. It does feel a bit odd though. On the user facing end, my worry with making the user mark final variables is that we start down a path to death by 1000 cuts. This proposal is trying to be highly opinionated, to get as much value for the common case as possible. Allowing mutable variables to be explicitly marked with |
In the Overview of a proposal for structs document summary it says:
I think structs shouldn't be immutable if C/C++ interoperability is desired. Imagine the scenario where a native library allocates a struct and Dart FFI returns a Dart struct backed by the same memory address. If the struct stores an int64 or a pointer address, then these values cannot be changed and may not be possible to change references in the native library to the new struct with the desired values. |
If we allow modification of member variables of a struct, we effectively prohibit some variants of unboxing. Objects have consistent identity, and that identity is preserved across assignment. C++ structs have identity, but copy semantics for assignment (and pointers/references for the cases where you want to share/alias a single identity). To meaningfully copy C++ behavior for Dart structs (which is not necessarily a goal), Dart would need to have copy semantics too. We could say that structs have identity when stored in a variable, and all accesses to the struct through the same variable accesses the same struct instance, but that assigning to another variable (including parameter passing and returning) does not need to preserve identity. It also goes the other way, we cannot make copies prematurely, which may prevent some unboxing. We can unbox a struct, but we must ensure that there are no other references to the same struct that are still alive, because then we won't preserve identity properly. Unboxing is copying. If we can prove that there is only a single boxed or unboxed version of a struct at any given time, then it's safe to allow modification. If we can't, we cannot unbox. Allowing modification risks blocking a lot of potentially valuable optimizations. It requires specifying a precise and predictable identity strategy that all implementations must follow. I'd prefer immutability, because it's extremely predictable. |
Two more advantages of immutability:
(By statically seeing that a struct's members are either structs or primitives, thereby making them transitively immutable) |
I would very much support having immutable struct entities, with no guaranteed identity, and supporting structural comparison (presumably by means of an operator As mentioned several times already, mutability implies identity, in the sense that it's impossible to write correct programs when two references to a mutable entity may or may not be aliases to the same entity, and you cannot know which. So if we wish to allow the compiler to decide when to box/unbox a struct entity then we have no choice. We could also use a C++ like semantics, and require that developers specify exactly how to manage the storage of struct entities. In that case they could be mutable. However, I do not think this kind of low-level memory management is a good match for Dart. I'd very much prefer to have a clear and safe semantics, and leave ample opportunities for compilers to optimize this kind of code heavily. |
I think struct A(int x);
struct B(int y, A a);
B b; How do you do an equivalent of It's hard to predict how often this would occur but it would be interesting to consider some variation of recursive record update syntax: @Wdestroier the concept of |
@mraleph Thanks 😊, just to be clear I'm fine with any decision about this... |
Yes, I think structs should be immutable. If you want a "mutable struct", what you probably really want is just a class with the brevity of a primary constructor. We should let you use primary constructors on classes too. |
The struct proposal requires that all fields in a struct/data class be final. The motivations around this are primarily around representation: one of the problems that I am aiming to solve is that we have a demonstrated need for object representations that allow the compiler to freely do scalar replacement of aggregates/unboxing optimizations that do not preserve identity (I believe that our SIMD classes already do this, in mild violation of the specified behavior). A secondary motivation is around brevity: based on the user feedback we've received around data classes, by far the common case that people wish to support is immutable data. Specifying that all fields in a struct are immutable allows the briefed
int x
field declaration form to be used instead of the more verbosefinal int x
form.Is this the right choice?
On the semantic end, allowing mutable fields + unboxing is very questionable. Aliasing of mutable objects must be predictable: if the compiler is free to unbox and re-materialize a single mutable object, chaos ensues. There are three possible solutions I see to this:
On the brevity end, I see (at least) three choices:
var
(e.g.var int x
)The text was updated successfully, but these errors were encountered: