www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Gotcha with const std.range.chunks

reply sportsracer <steffenwenz+d gmail.com> writes:
Newcomer to the D language here. I was going to use 
`std.range.chunks` to get a two-dimensional, read-only view on 
data in one continuous array.

But was surprised to find that this code doesn't compile:

import std.range : chunks;
import std.stdio : writeln;

void main()
{
     int[] xs = new int[100];
     const chunked = xs.chunks(10);
     writeln(chunked[0][0]);
}

Error: mutable method std.range.Chunks!(int[]).Chunks.opIndex is 
not callable using a const object

… I'm not sure if this is due to my ignorance of the D type 
system, or could be considered missing functionality in the 
chunks helper function.
Jul 30 2020
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jul 30, 2020 at 06:52:42PM +0000, sportsracer via Digitalmars-d-learn
wrote:
[...]
     int[] xs = new int[100];
     const chunked = xs.chunks(10);
     writeln(chunked[0][0]);
 }
 
 Error: mutable method std.range.Chunks!(int[]).Chunks.opIndex is not
 callable using a const object
In order to iterate the chunks, you must be able to mutate the range object returned by .chunks. If it's const, then it's not mutable, and therefore it cannot be iterated. If you want to protect the underlying data from mutation, create a const view of the data first, e.g., something like this: int[] xs = new int[100]; const(int)[] constView = xs; auto chunked = constView.chunks(10); // N.B. mutable The range itself will be mutable, but the elements will be const and not mutable through the range. T -- Why can't you just be a nonconformist like everyone else? -- YHL
Jul 30 2020
parent sportsracer <steffenwenz+d gmail.com> writes:
Right, thank you! The range object needs to be mutated to be 
iterated on, that explains it.

Although it looks like the opIndex on this particular range could 
guarantee constness: 
https://github.com/dlang/phobos/blob/v2.093.0/std/range/package.d#L8099

Here's a longer code snippet that gives some context on why I 
thought I needed a const range object. This Matrix class would 
have the chunks range object stored as a private property, and 
use it in its opIndex to look up values. I wanted to add the 
const qualifier to the opIndex method, since it doesn't mutate 
the Matrix state, and that's what surfaced this error.


class Matrix(T)
{
     private {
         T[] data;
         Chunks!(T[]) view;
     }

     this(size_t width, size_t height)
     {
         this.data = new int[width * height];
         this.view = data.chunks(width);
     }

     T opIndex(size_t col, size_t row) /*const*/ // FIXME Can't be 
const because Chunks.opIndex isn't
     {
         return view[row][col];
     }
}

void main()
{
     auto matrix = new Matrix!int(10, 10);
     writeln(matrix[0, 0]);
}


Oh well, I'll try a different way!
Jul 31 2020