Why D const is annoying

Jonathan M Davis jmdavisProg at gmx.com
Sat Dec 10 12:25:25 PST 2011


On Saturday, December 10, 2011 05:45:11 bearophile wrote:
> Timon Gehr:
> > Just slice the const array to get a range. The specialization for ranges
> > does not have the bug.
> > 
> > import std.algorithm;
> > void main() {
> > 
> >      const arr = [1, 2, 3];
> >      reduce!"a*b"(arr[]);   // It works.
> > 
> > }
> 
> Wasn't arr a range already?

Per isInputRange!(typeof(arr)), no. It has the right functions, but it can't 
use them, because it's const. A const range is essentially useless, because 
you can't iterate over it.

When a template is instantiated, it's instantiated on the exact types that 
it's given. So, if you given a const or immutable array, it's going to 
instantiate on that type, even though it _could_ theoretically instantiate 
itself with a mutable array with const or immutable elements. And since, a 
const or immutable array won't work as a range, the template instantiation 
fails.

The range-based functions in std.array and std.string work with const and 
immutable arrays simply because they are either coded specifically for arrays 
or have specializations for them. You need a function which takes const(T)[] 
rather than one which takes const T[]. std.array and std.string typically take 
const(T)[] rather than const T[], whereas a more general range function is 
taking R, which in the case above is determined to be const int[], which won't 
work as a range.

It used to be that the slice of an array was the exact type of that array, 
meaning Timon's solution wouldn't work without a cast, because the slice would 
be just as const as the original. Fortunately, that has been changed, so now a 
slice of a const or immutable array will have its elements be const or 
immutable but not the slice itself. So, now const and immutable arrays are 
like static arrays in that you need to slice them to use them with range-based 
functions, but you can't use them directly with range-based functions.

A bigger problem is const or immutable ranges which are structs. Unless the 
programmer who defined the range type managed to have an opSlice which returned 
a version with const or immutable elements but where the range itself wasn't 
const or immutable (i.e. a tail-const slice), then you can't even get slicing 
to work. And even if templates were improved to the point that they would 
instantiate const int[] as const(int)[] (which I expect is a change which will 
never happen due to the difficulties in doing so), that wouldn't solve the 
problem for ranges which aren't arrays.

Also, const and immutable ranges which aren't arrays which have no opSlice 
will _never_ work, because there's no way to get a mutable slice of them to 
operate on, so you're stuck with a const or immutable range.

Really what this means is that you need to slice const and immutable ranges 
when you pass them to range-based functions, and then as long as they're 
arrays or their opSlices have been defined properly, it'll work just fine.

If ranges had been designed more like slists (i.e. they have head and tail 
rather than front and popFront), then they would have been inherently tail-
const and we wouldn't be having these problems. But that's less efficent, 
because you have to copy the range every time that you want to remove an 
element from it. Regardless, it's too late now. Ranges are too heavily used 
with their current API for us to change it that drastically now.

- Jonathan M Davis


More information about the Digitalmars-d mailing list