www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Copying with immutable arrays

reply "Tobias Pankrath" <tobias pankrath.net> writes:
So I have this immutable array with user defined structs but I 
can not
make a copy from it.

See:

struct SwA {
     string[] strings;
}

void main()
{
     immutable(SwA)[] arr1;
     SwA[] arr2 = arr1.dup;
}

Says:
Error: cannot implicitly convert element type immutable(SwA) to 
mutable in arr1.dup

Next try:

struct SwA {
     string[] strings;
}

void main()
{
     import std.algorithm;
     immutable(SwA)[] arr1;
     SwA[] arr2;
     copy(arr1, arr2);
}

Says:
test.d(11): Error: template std.algorithm.copy does not match any 
function template declaration
/home/tobias/projekte/d/dmd/src/../../phobos/std/algorithm.d(5859): 
Error: template std.algorithm.copy(Range1,Range2) if 
(isInputRange!(Range1) && 
isOutputRange!(Range2,ElementType!(Range1))) cannot deduce 
template function from argument types !()(immutable(SwA)[],SwA[])

(Which is a bug, I guess).

So can I do this without a cast?
Oct 27 2012
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/27/2012 02:30 AM, Tobias Pankrath wrote:
 So I have this immutable array with user defined structs but I can not
 make a copy from it.

 See:

 struct SwA {
 string[] strings;
 }

 void main()
 {
 immutable(SwA)[] arr1;
 SwA[] arr2 = arr1.dup;
 }

 Says:
 Error: cannot implicitly convert element type immutable(SwA) to mutable
 in arr1.dup

 Next try:

 struct SwA {
 string[] strings;
 }

 void main()
 {
 import std.algorithm;
 immutable(SwA)[] arr1;
 SwA[] arr2;
 copy(arr1, arr2);
 }

 Says:
 test.d(11): Error: template std.algorithm.copy does not match any
 function template declaration
 /home/tobias/projekte/d/dmd/src/../../phobos/std/algorithm.d(5859):
 Error: template std.algorithm.copy(Range1,Range2) if
 (isInputRange!(Range1) && isOutputRange!(Range2,ElementType!(Range1)))
 cannot deduce template function from argument types
 !()(immutable(SwA)[],SwA[])

 (Which is a bug, I guess).

 So can I do this without a cast?
Casting may not do the right thing, as the immutable object would be confused if the mutable object modify members. Copying a struct produces an object of its own type. If the original is an immutable SwA, then the copy is an immutable SwA. That's why the compiler cannot produce a mutable SwA automatically. After the naming convention of arrays, objects can be copied by an explicit dup() function: struct SwA { string[] strings; SwA dup() const property { auto result = SwA(strings.dup); return result; } } (Apologies if the following is obvious after that.) Now the following compiles: auto i = immutable(SwA)([ "a", "b" ]); SwA m = i.dup; And as expected, the following would compile as well: m.strings ~= "c"; With that, to produce mutable elements from an array of immutable(SwA), we can use map() and if the mutable elements need not be put into an array eagerly, the following is sufficient: auto arr1 = [ immutable(SwA)([ "a", "b" ]), immutable(SwA)([ "x", "y" ]) ]; auto arr2 = arr1.map!(e => e.dup); writeln(arr2); arr2 is a range of mutable SwA objects that can be passed to other range algorithms, even to writeln. (I am pretty sure the produced SwA objects are rvalues as they come out of map.) The output is: [SwA(["a", "b"]), SwA(["x", "y"])] And of course, if needed, arr2 could have been an actual array by calling array(): import std.array; // ... auto arr2 = arr1.map!(e => e.dup).array; Here is the whole program: struct SwA { string[] strings; SwA dup() const property { auto result = SwA(strings.dup); return result; } } import std.stdio; import std.algorithm; import std.array; void main() { auto i = immutable(SwA)([ "a", "b" ]); SwA m = i.dup; m.strings ~= "c"; auto arr1 = [ immutable(SwA)([ "a", "b" ]), immutable(SwA)([ "x", "y" ]) ]; auto arr2 = arr1.map!(e => e.dup); writeln(arr2); } Ali
Oct 27 2012
parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
Thank you for your detailed answer!

What I don't understand is the reason why you need the .dup in 
the first place.

Take a look at these two structs that now contain an int* and an 
int instead of string[].

struct SA {
     int i;
}

struct SB {
     int* i;
}

If you try to .dup an array of SA it will work and if you .dup an 
array of SB it will fail. I understand why it works this way, 
because in case of SB you would get an mutable reference of type 
int* out of immutable(int*), which would brake the type system. 
However the struct SwA from above does neither correspond to SA 
nor to SB, it's imo more like SC:

struct SC {
     immutable(int)* i;
}

Now a copy would do no harm, everything that was immutable in the 
source and is still accessable from the copy is immutable, too. 
Any reason (despite of implemenational issues) that this is not 
how it works?
Oct 28 2012
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/28/2012 02:37 AM, Tobias Pankrath wrote:
 the struct
 SwA from above does neither correspond to SA nor to SB, it's imo more
 like SC:

 struct SC {
 immutable(int)* i;
 }
Just to confirm, the above indeed works: struct SC { immutable(int)* i; } void main() { immutable(SC)[] arr1; SC[] arr2 = arr1.dup; // compiles }
 Now a copy would do no harm, everything that was immutable in the source
 and is still accessable from the copy is immutable, too. Any reason
 (despite of implemenational issues) that this is not how it works?
Getting back to the original code, the issue boils down to whether we can copy imutable(string[]) to string[]: import std.stdio; struct SwA { string[] strings; } void main() { immutable(SwA)[] arr1; writeln(typeid(arr1[0].strings)); } The program prints the following: immutable(immutable(immutable(char)[])[]) Translating the innermost definition as string: immutable(immutable(string)[]) Let's remove the struct and look at a variable the same type as the member: immutable(string[]) imm = [ "a", "b" ]; writeln(typeid(imm)); The typeid is the same: immutable(immutable(immutable(char)[])[]) So we can concentrate on 'imm' for this exercise. This is the same compilation error: immutable(string[]) imm = [ "aaa", "bbb" ]; string[] mut = imm; // <-- compilation ERROR If that compiled, then both 'imm' and 'mut' would be providing access to the same set of strings. But the problem is, 'mut' could replace those strings (note that it could not modify the characters of those strings, but it could replace the whole string): mut[0] = "hello"; That would effect 'imm' as well. ('imm' is the equivalent of SwA.strings from your original code.) Ali
Oct 28 2012
next sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
On Monday, 29 October 2012 at 06:19:36 UTC, Ali Çehreli wrote:
 Just to confirm, the above indeed works:

 struct SC {
     immutable(int)* i;
 }

 void main()
 {
     immutable(SC)[] arr1;
     SC[] arr2 = arr1.dup;    // compiles
 }
Oh, I thought I've tried it.
 [snip] [Really detailed description here] [snip]
Thanks, now I've got it :-)
Oct 29 2012
prev sibling parent Don Clugston <dac nospam.com> writes:
On 29/10/12 07:19, Ali Çehreli wrote:
 On 10/28/2012 02:37 AM, Tobias Pankrath wrote:
  > the struct
  > SwA from above does neither correspond to SA nor to SB, it's imo more
  > like SC:
  >
  > struct SC {
  > immutable(int)* i;
  > }

 Just to confirm, the above indeed works:

 struct SC {
      immutable(int)* i;
 }

 void main()
 {
      immutable(SC)[] arr1;
      SC[] arr2 = arr1.dup;    // compiles
 }

  > Now a copy would do no harm, everything that was immutable in the source
  > and is still accessable from the copy is immutable, too. Any reason
  > (despite of implemenational issues) that this is not how it works?

 Getting back to the original code, the issue boils down to whether we
 can copy imutable(string[]) to string[]:

 import std.stdio;

 struct SwA {
      string[] strings;
 }

 void main()
 {
      immutable(SwA)[] arr1;
      writeln(typeid(arr1[0].strings));
 }

 The program prints the following:

 immutable(immutable(immutable(char)[])[])

 Translating the innermost definition as string:

 immutable(immutable(string)[])

 Let's remove the struct and look at a variable the same type as the member:

      immutable(string[]) imm = [ "a", "b" ];
      writeln(typeid(imm));

 The typeid is the same:

 immutable(immutable(immutable(char)[])[])

 So we can concentrate on 'imm' for this exercise. This is the same
 compilation error:

      immutable(string[]) imm = [ "aaa", "bbb" ];
      string[] mut = imm;       // <-- compilation ERROR

 If that compiled, then both 'imm' and 'mut' would be providing access to
 the same set of strings. But the problem is, 'mut' could replace those
 strings (note that it could not modify the characters of those strings,
 but it could replace the whole string):

      mut[0] = "hello";

 That would effect 'imm' as well. ('imm' is the equivalent of SwA.strings
 from your original code.)

 Ali
Awesome answer, it's these kinds of responses that make the D community so great.
Oct 30 2012