Before I take on the effort of writing up and submitting a DIP, let me solicit feedback and see if anyone can see a reason why this idea is dumb and doesn't work.
tl;dr: immutable struct
was a mistake: it's too weak. rvalue struct
is what we really want.
aside: what are lvalues and rvalues
lvalue: an expression that has an address.
rvalue: an expression that does not.
The names come from a = b
: an "lvalue" (left value) is a thing that can appear on the left of the equals sign, an "rvalue" (right value) may only appear on the right.
Example: int a
is an lvalue. 5
is an rvalue. You can write a = 5
but not 5 = a
.
rvalue struct
This is a summary of an idea from my DConf talk https://www.youtube.com/watch?v=eGX_fxlig8I
I'm writing it up because I'm noticing that the immutable
bugs are neverending and the workarounds are an endless hole into which effort and nerves are thrown to no perceivable change.
What is it? Take this struct
rvalue struct S
{
int a;
int[] b;
}
Two rules:
- A
rvalue
struct hasimmutable
fields.
typeof(s)
isS
, buttypeof(s.b)
isimmutable int[]
. - Any symbol that would be an lvalue to a field of
S
is instead an rvalue.
That's it.
What is the effect of this?
This works:
S foo(S s) {
S value = S.init;
value = s;
*&value = s;
return s;
}
This does not:
S s;
s.a = 5;
&s.a
((ref int i) {})(s.a);
Note that you can overwrite value
all you want. You can take the address of value
. It's not immutable. But its fields are immutable. Isn't that a problem? No: because its fields don't exist. They're rvalues. You can't address them, you can't reference them. You can never observe a constness violation on them, because you can only observe them as rvalues. s.a
is effectively an accessor.
What's the use?
D libraries like to behave like they can declare variables and assign values to them. (Oh, to live in such innocence!) This is all over Phobos, Dub, etc. immutable struct
frustrates this belief. Because you could always take the address of a field, which would be immutable T*
, you could see the value changing when you overwrite the variable - a constness violation. immutable
solves this by preventing you from modifying the memory of the field while the pointer lives. This largely doesn't work, because people don't test with immutable struct
in the first place. If an rvalue struct
is used, the naive code works as before, but the type gets the correctness benefits of immutable: you can only construct a new value through the constructor.
Corporate Motivation
Some details about Funkwerk, fresh off wc -l
: In the modern part of our codebase, we have 648 domain structs, out of which 278 are immutable struct
, and at least another 129 are good candidates to make immutable. In those 113kloc, we have no pointers to struct fields. None. immutable struct
puts on the language, on library developers and on end users, immense difficulty and effort to protect a usecase that isn't useful.