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