digitalmars.D.learn - Copying array with const correctness
- Vindex9 (40/40) Oct 07 I'm trying to write a function for copying an array. But
- Vindex9 (47/47) Oct 07 I’d like to clarify: the original goal was to create an
- Vindex9 (2/3) Oct 07 * alternative to dup property
- Vindex9 (17/17) Oct 07 So far, the simplest solution is to create a separate function
- Steven Schveighoffer (50/52) Oct 07 I think the approach should be, make a copy, then cast the copy,
- Vindex9 (65/71) Oct 08 Unfortunately, `Unqual` in your code doesn't do anything - the
- Vindex9 (24/24) Oct 08 Here's a slightly better solution. The lines will be copied.
- Vindex9 (23/23) Oct 08 More accurately:
- Steven Schveighoffer (7/21) Oct 08 You are right, I didn't think about the fact that `T` is going to
I'm trying to write a function for copying an array. But
something strange is happening with the const correctness. I
cannot remove the constitution for the case of a two-dimensional
array of lines.
```d
import std.stdio;
import std.traits : Unconst;
auto copyArray(T)(const T[] arr) if (!is(T == class)) {
alias E = Unconst!T;
E[] copy = new E[arr.length];
writeln("orig type: ", typeid(T)); // orig type:
const(immutable(char)[])[]
writeln("copy type: ", typeid(E)); // copy type:
const(immutable(char)[])[]
writeln("copy type: ", typeid(Unconst!E)); // copy type:
const(immutable(char)[])[]
static if (is(T : const(E)[])) { // if inner array (doesn't
work)
writeln("No");
for (size_t i = 0; i < arr.length; i++) {
copy[i] = copyArray(arr[i]);
}
} else {
for (size_t i = 0; i < arr.length; i++) {
E elem = arr[i];
copy[i] = elem;
}
}
return copy;
}
void main() {
const string[][] arr = [["ABC", "DEF"], ["GHI", "JKL"]];
writeln(typeid(arr)); //
const(const(const(immutable(char)[])[])[])
auto res = copyArray(arr);
writeln(typeid(res)); // const(immutable(char)[])[][]
}
```
What am I doing wrong?
I want a safe copy: `const string[][] -> string[][]`.
Oct 07
I’d like to clarify: the original goal was to create an
alternative to Dub in order to make copies of arrays of objects
that contain copying constructors.
It's all good like this:
```d
unittest
{
T[] copyArray(T)(const T[] arr) if (!is(T == class)) {
T[] copy = new T[arr.length];
for (size_t i = 0; i < arr.length; i++) {
// it doesn't work with postblit, only with modern
copy ctor
T elem = arr[i];
copy[i] = elem;
}
return copy;
}
struct S {
int x, y;
bool[] a;
this(ref return scope const S rhs) {
this.x = rhs.x;
this.y = rhs.y;
this.a = rhs.a.dup;
}
}
const S[] arr = [
S(1, 2, [true, false]),
S(3, 4, [false, true])
];
// S[] copy = arr.dup; // It can't be compiled!
S[] copy = copyArray(arr); // from const array to non-const
one
assert(copy.length == arr.length);
for (size_t i = 0; i < arr.length; i++) {
assert(arr[i].x == copy[i].x);
assert(arr[i].y == copy[i].y);
assert(arr[i].a == copy[i].a);
assert(arr[i].a.ptr != copy[i].a.ptr);
}
const S[] emptyArr = [];
assert(emptyArr.ptr == null);
S[] emptyCopy = copyArray(emptyArr);
assert(emptyCopy.ptr == null);
}
```
The problems started when I decided to increase the versatility.
Oct 07
On Tuesday, 7 October 2025 at 19:07:28 UTC, Vindex9 wrote:alternative to Dub* alternative to dup property
Oct 07
So far, the simplest solution is to create a separate function
for a two-dimensional array:
```d
auto copy2DArray(T)(const T[][] arr) if (!is(T == class)) {
T[][] copy;
foreach(row; arr) {
T[] tmp;
foreach(field; row) {
tmp ~= field;
}
copy ~= tmp;
}
return copy;
}
```
However, the metamorphoses of the types from my first example are
very mysterious.
Oct 07
On Tuesday, 7 October 2025 at 18:43:18 UTC, Vindex9 wrote:What am I doing wrong? I want a safe copy: `const string[][] -> string[][]`.I think the approach should be, make a copy, then cast the copy, recurse if it's a nested array. What you are likely running into is that D automatically strips the top-level const from a templated array. That is: ```d void foo(T)(T[] arr) { pragma(msg, "in foo: ", typeof(arr)); // const(int)[] } void main() { const int[] arr; pragma(msg, "in main: ", typeof(arr)); // const(int[]) foo(arr); } ``` On top of that, you are adding `const` to the incoming type, which can be problematic. What I'd recommend is `inout` instead. This will unwrap to the original type but still keep you from modifying the original (like const). My attempt: ```d import std.traits; inout(T)[] copyArray(T)(inout(T)[] arr) { alias M = Unqual!T; M[] result; result.length = arr.length; foreach(i, ref v; result) { static if(is(T == U[], U)) // it's an array of arrays v = cast(M)copyArray(arr[i]); else v = cast(M)arr[i]; } return cast(typeof(return))result; } void main() { import std.stdio; const string[][] arr = [["ABC", "DEF"], ["GHI", "JKL"]]; writeln(typeid(arr)); // const(const(const(immutable(char)[])[])[]) const res = copyArray(arr); // note the const here, but still typesafe without! writeln(typeid(res)); // const(const(const(immutable(char)[])[])[]) } ``` -Steve
Oct 07
On Wednesday, 8 October 2025 at 02:58:15 UTC, Steven
Schveighoffer wrote:
My attempt:
```d
import std.traits;
inout(T)[] copyArray(T)(inout(T)[] arr) {
alias M = Unqual!T;
```
Unfortunately, `Unqual` in your code doesn't do anything - the
type `M` remains `T`. Apparently, some strange things are
happening inside the template when it comes to constness.
Sometimes you need to return an array from a const method. You
don't want modifications to the returned array to affect the
state of the struct, but at the same time, you want to freely
manipulate the consents of that array. That's why removing
constness is important to me. However, it seems that making a
recursive copy isn't necessary: when it comes to strings, we
don't need to turn a `string` into a `char[]`. As s way out, we
can simply avoid removing constness from `immutable` data. It
seems I have managed to reach the goal without resorting to
explicit conversions.
```d
import std.stdio;
T[] copyArray(T)(inout(T)[] arr) {
T[] result;
result.length = arr.length;
static if(is(T == U[], U) && !is(T == immutable(Y)[], Y)) {
foreach(i, ref v; result) {
v = copyArray(arr[i]);
}
} else {
foreach(i, ref v; result) {
T middle = arr[i];
v = middle;
}
}
return result;
}
struct S {
int x;
bool[] a;
this(ref return scope const S rhs) {
this.x = rhs.x;
this.a = rhs.a.dup;
}
}
void main() {
const string[] arr1d = ["ABC", "DEF"]; //
const(const(immutable(char)[])[])
writeln(typeid(arr1d));
auto res1 = copyArray(arr1d);
writeln(typeid(res1)); // immutable(char)[][]
const string[][] arr2d = [["ABC", "DEF"], ["GHI", "JKL"]];
writeln(typeid(arr2d)); //
const(const(const(immutable(char)[])[])[])
auto res2 = copyArray(arr2d);
writeln(typeid(res2)); // immutable(char)[][][]
const S[] structArr = [S(8, [true, false]), S(9, [false,
true])];
writeln(typeid(structArr)); // const(const(onlineapp.S)[])
auto structArrCopy = copyArray(structArr);
writeln(structArrCopy);
writeln(typeid(structArrCopy)); // onlineapp.S[]
const int[][] intArr = [[1, 2], [3, 4]];
writeln(typeid(intArr)); // const(const(const(int)[])[])
auto intArrCopy = copyArray(intArr);
writeln(intArrCopy);
writeln(typeid(intArrCopy)); // int[][]
}
```
Thank you, Steve.
Oct 08
Here's a slightly better solution. The lines will be copied.
```d
T[] copyArray(T)(inout(T)[] arr) {
T[] copy = new T[arr.length];
copy.length = arr.length;
static if (is(T == U[], U) && !is(T == immutable(Y)[], Y)) {
foreach(i, ref v; copy) {
v = copyArray(arr[i]);
}
} else {
for (size_t i = 0; i < arr.length; i++) {
static if (is(T == immutable(Y)[], Y)) {
copy[i] = arr[i].idup;
} else {
// it doesn't work with postblit, only with
modern copy ctor
T elem = arr[i];
copy[i] = elem;
}
}
}
return copy;
}
```
Oct 08
More accurately:
```d
T[] copyArray(T)(inout(T)[] arr) {
T[] copy = new T[arr.length];
static if (is(T == U[], U) && !is(T == immutable(Y)[], Y)) {
foreach(i, ref v; copy) {
v = copyArray(arr[i]);
}
} else static if (is(T == immutable(W)[], W)) {
for (size_t i = 0; i < arr.length; i++) {
copy[i] = arr[i].idup;
}
} else {
for (size_t i = 0; i < arr.length; i++) {
// it doesn't work with postblit, only with modern
copy ctor
T elem = arr[i];
copy[i] = elem;
}
}
return copy;
}
```
Oct 08
On Wednesday, 8 October 2025 at 07:46:32 UTC, Vindex9 wrote:On Wednesday, 8 October 2025 at 02:58:15 UTC, Steven Schveighoffer wrote:You are right, I didn't think about the fact that `T` is going to already be unmodified (since `inout` takes over the modifier). So really, this isn't needed, you can just use T, and rely on the explicit conversion at the end.My attempt: ```d import std.traits; inout(T)[] copyArray(T)(inout(T)[] arr) { alias M = Unqual!T; ```Unfortunately, `Unqual` in your code doesn't do anything - the type `M` remains `T`. Apparently, some strange things are happening inside the template when it comes to constness.Thank you, Steve.I'm glad you were able to work it out. -Steve
Oct 08









Vindex9 <tech.vindex gmail.com> 