On Mon, Mar 05, 2018 at 03:57:35AM -0700, Jonathan M Davis via Digitalmars-d-announce wrote: > Here's something I wrote up on const: > > http://jmdavisprog.com/articles/why-const-sucks.html > > I suppose that it's not exactly the most positive article, but I feel > that it's accurate. [...]
Yeah, I found myself in the same boat recently. As you often say, const and ranges simply don't mix. And modern idiomatic D is 90% about ranges, so that alone instantly reduces the scope of const's usefulness by a lot. A case in point: I was implementing a container recently, and wrote an opSlice() method to return a range over its elements. My initial thought was that it could be made const, since the range wouldn't mutate the underlying elements, but only represent a mutable slice over a const container, i.e., the slice can mutate, but the elements cannot, just like iterating over string (== immutable(char)[]). Should be easy, right? struct Container { auto opSlice() const { static struct Result { private Container impl; private int n; // internal mutable state @property bool empty() { ... } ... // rest of range API } return Result(this); } } Well, that didn't work, because in opSlice, `this` is const, and I can't initialize Result.impl which is a mutable Container. Well, no biggie, just make it const: struct Container { auto opSlice() const { static struct Result { private const(Container) impl; private int n; // internal mutable state @property bool empty() { ... } ... // rest of range API } return Result(this); } } At first, this worked, and it seems that I could have my const cake and eat it too. Until I decided at some point that I needed to make it a forward range, which requires a .save method. So I tried: ... static struct Result { private const(Container) impl; ... @property Result save() { Result copy = this; return this; } } That seemed to do the trick, everything compiles and works. But I soon ran into an unfixable problem: the resulting range, while it works in the simplest cases, started causing mysterious compile errors when I tried to use it with Phobos range algorithms. Eventually, I discovered that the underlying problem was that Result, as defined above, was a struct with a const member, and therefore it was illegal to assign it to a variable of the same type outside of initialization (since doing do meant you were overwriting a const field with something else, which violates the constness of the field). This broke the by-value assumption inherent in much of Phobos code, so the resulting range ended being unusable with most Phobos algorithms. Which defeated the whole purpose in the first place. While I'm sure with enough time and patience Phobos could be fixed to support this kind of range, the decision I was faced with was: should I (1) persist in using const, and thereby stall my project while I work on huge chunks of Phobos range algorithms to make them usable with ranges that have const members (not to mention spending how much time waiting in the Phobos PR queue and potentially getting things rejected because it might cause breakage of unknown amounts of existing code), or (2) just remove `const` from my code, and be able to continue with my project *right now*? The choice was a no-brainer, sad to say. So yeah, while D's const provides actual guarantees unlike C++'s laughable const-by-documentation, that also limits its scope so much that in practice, it's rarely ever used outside of built-in types like string. Which also limits the usefulness of its guarantees so much that it's questionable whether it's actually worth the effort. T -- It won't be covered in the book. The source code has to be useful for something, after all. -- Larry Wall