First Draft: ref For Variable Declarations
Witold Baryluk
witold.baryluk at gmail.com
Sun Apr 14 00:04:13 UTC 2024
On Friday, 12 April 2024 at 20:43:50 UTC, Walter Bright wrote:
> https://github.com/WalterBright/documents/blob/984374ca885e1cb10c2667cf872aebc13b4c1663/varRef.md
I am not exactly sure what is this solving, or how it improves
code. And DIP draft does not exactly explain that either.
I am afraid D code will then become C++-like, and be riddled with
all these refs.
For the last 20 years I was actively avoiding using references in
C++ code, (with only exception of `const&` being ok, only because
this avoids `->` in C++, and implicitly in general tells me that
pointer ownership is not being transferred to the called
function). And I have seen many companies with large code bases
doing the same. One of the main reasons for me to avoid it, is
that it is obstructing call site by making things implicit. One
could say similar things about `lazy`, but I think that is fine,
as it is used in way more narrow context. Another reason to avoid
are reassignment semantics, and complex interactions with
construction, destruction, which are non trivial.
From all C++ devs that I do know in professional setting (most of
them 10-20 years writing C++ code every day), in fact not a
single one of them, know exact semantics of C++ references. And
anybody who said they do for sure, in fact didn't.
In C++, I didn't write a single ref variable in my life (by
choice), other than while debugging others code. And not a single
(non-const) ref parameter. And it never was limiting me or caused
readability problems (quite the contrary actually).
(Usage of `&` in lambda capture is fine, as there is no other
way, and this is useful. But in D, things are currently inferred
automatically by compiler. Plus in lambda capture semantic is
pretty clear and very rarely abused in a way that makes code hard
to follow).
I would say that ref variables is the worst C++ feature from all
its "features".
In C++, they are kind of needed sometimes, i.e. for example when
implementing mutable containers, to implement things like `x[foo]
+= 1`, but it can be abused (i.e. if `x[foo]` is assigned to
`auto &z` somewhere first, then one mutates `x` in a way, that
`z` becomes invalid reference, and only then you access `z`. But
in D, we have nicer and more power ways to deal with things like
`x[foo] += 1` using `op*` family of function. So we do not need
these kinds of references most of the time. (I do know some
people do use non-const ref return values for functions in D. But
it is not exactly needed most of the time, or can be implemented
using wrapper structs with some kind of forwarding if really
really required).
As of safety. I would say references, as they are in C++, make
C++ less safe. Exactly because of extra complications,
implicitness, and other issues.
In D, readability is even less of an issue for pointers, as `->`
is not required, and one will use a `.`, which is nice, and one
will sporadically see maybe something like `(*x) =`, or similar
(rarely, as this is usually used for out parameters for returning
multiple values, but that can be easily done with `out` or some
multi-value return / tuple return maybe). And using `*x = ` is in
fact pretty nice to remind you (and code reviewer, or future you)
what is happening exactly.
I am not totally against (it is a choice to use or not use this
future), but if it proliferates into libraries (or phobos!), I
would be rather disappointed for me personally.
And yes, I do like and use `const ref` in D sometimes (i.e. for
fixed arrays, or some structs), and sporadically `ref` in
`foreach` which is handy. Still I not needed, I don't. I.e. I
trust compiler to not copy some structs in array if I do not
modify them for example.
And for classes everything is by "ref" (the object itself, not
the variable), so that mostly avoid copying issues, that C++
needs to deal with.
More concretely. Example you show
```c++
ref int dark(ref int x, int i) {
ref j = i; // j now points to i
j = 3; // now i is 3 as well
...
```
and I am already lost basically. Lets say one has very slightly
modified same code:
```c++
struct A {
~this() {
....
}
}
ref int dark(ref A x, A i) {
ref j = i; // j now points to i
j = A(3); // now i is 3 as well
...
```
And now, I need to think very hard to know what is actually
happening. Or even miss the fact that my assignment probably
caused call to `A`s destructor. And god forgive there were other
references (by pointers) to object in `j` previously.
I do not like implicit mechanisms that are not easy to track
without extra context.
And I do fail to see why one would complicating language and
implementation for very small gain, where in very rare cases
where one there would be benefit, doing it explicitly using
pointers is in fact better and cleaner by being explicit. If
compiler could fully prove correctness, than maybe I could see
some benefit, but good luck with that.
You can also see languages like Go, where there are pointers, and
no references. And they are perfectly happy with that, with
gazillion of libraries showing it is not really an issue.
Regards.
More information about the dip.development
mailing list