www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - bug or feature?

reply bobef <be lessequal_dontspam.com> writes:
import std.stdio;
import std.string;

void main()
{
	char[] a;
	char[][] b;
	a~="1234";
	b~=a;
	a.length=0;
	a~="&&";
	b~=a;
	a.length=0;
	a~="asdf";
	b~=a;
	writefln(join(b," "));
}

//outputs "asdf as asdf"

//if we (replace a~="..." with a="...") || (replace a.length=0 with 
a=null) || (replace b~=a with b~=a.dup) then it outputs "1234 && asdf"

//i don't know if this is bug or new feature since last time i used d
Dec 05 2006
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
bobef wrote:
 import std.stdio;
 import std.string;
 
 void main()
 {
     char[] a;
     char[][] b;
     a~="1234";
     b~=a;
     a.length=0;
     a~="&&";
     b~=a;
     a.length=0;
     a~="asdf";
     b~=a;
     writefln(join(b," "));
 }
 
 //outputs "asdf as asdf"
 
 //if we (replace a~="..." with a="...") || (replace a.length=0 with 
 a=null) || (replace b~=a with b~=a.dup) then it outputs "1234 && asdf"
 
 //i don't know if this is bug or new feature since last time i used d

Its a side-effect of a feature, I believe. Setting an array's length to 0 no longer deallocates the array. And the ~= in this case is storing the current whole /slice/ of a into b. So its by design, but definitely a potential gotcha! -- Chris Nicholson-Sauls
Dec 05 2006
parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Chris Nicholson-Sauls wrote:
 bobef wrote:
 import std.stdio;
 import std.string;

 void main()
 {
     char[] a;
     char[][] b;
     a~="1234";
     b~=a;
     a.length=0;
     a~="&&";
     b~=a;
     a.length=0;
     a~="asdf";
     b~=a;
     writefln(join(b," "));
 }

 //outputs "asdf as asdf"

 //if we (replace a~="..." with a="...") || (replace a.length=0 with 
 a=null) || (replace b~=a with b~=a.dup) then it outputs "1234 && asdf"

 //i don't know if this is bug or new feature since last time i used d

Its a side-effect of a feature, I believe. Setting an array's length to 0 no longer deallocates the array. And the ~= in this case is storing the current whole /slice/ of a into b. So its by design, but definitely a potential gotcha!

It is by design and works as intended. And you are right - it is a gotcha that every new user to D must be aware of. The above example is excellent in illustrating this. The fundamental reason for this is that D has combined the two abstract concepts, the array and the slice, as a hybrid: T[] Those two concepts are unfortunately not 100 % compatible and this leads to problems such as the one above. I'm sure this design decision will come back and haunt D many times in the future. To most people, the abstract concept of an array is "a set of elements with a sequential order". D's dynamic array (T[]) has the side effect of when changing, removing or adding elements in one array, other arrays /may/ be altered. This must be surprising for anyone with only an abstract array model in mind (a sequential set of elements). There is only one sure way to prevent this - to strictly and manually employ Copy on Write for all cases except when you can be sure that the memory area used by the array is unique and writable. There is no help from the compiler. D will not keep reference counts, give compile time or run time warnings or even hold your hand. It even prevents you from implementing your own automatic reference counts. The mantra is "If unsure, dup." The above code violates CoW twice, and thus the unexpected behavior. /Oskar
Dec 05 2006
prev sibling parent Derek Parnell <derek psych.ward> writes:
On Tue, 05 Dec 2006 15:56:25 +0200, bobef wrote:


 
 //outputs "asdf as asdf"

That is because setting the length to zero does not deallocate the RM for the array any more. What you are getting is three slices of 'a' in 'b'. Here is your code with this shown ... import std.stdio; import std.string; void main() { char[] a; char[][] b; a~="1234"; writefln("%s[%s]", a.ptr, a.length); b~=a; a.length = 0; a~="&&"; writefln("%s[%s]", a.ptr, a.length); b~=a; a.length = 0; a~="asdf"; writefln("%s[%s]", a.ptr, a.length); b~=a; writefln(join(b," ")); } Outputs: 870FE0[4] 870FE0[2] 870FE0[4] asdf as asdf
 //if we (replace a~="..." with a="...") || (replace a.length=0 with 
 a=null) || (replace b~=a with b~=a.dup) then it outputs "1234 && asdf"

The first two ensure that the array is reallocated on each assignment. The last option explcitly takes a copy of the slice into 'b'. I'm not sure what you were hoping to achieve with this type of coding. The simple solution I think is the first alternative that you give. import std.stdio; import std.string; void main() { char[] a; char[][] b; a="1234"; b~=a; a="&&"; b~=a; a="asdf"; b~=a; writefln(join(b," ")); }
 //i don't know if this is bug or new feature since last time i used d

Definitely a feature. If your code was trying to avoid multiple allocation for the array, here is one way that can be done now ... import std.stdio; import std.string; void main() { char[] a; char[][] b; int i; // Allocate an initial buffer size a.length = 200; writefln("%s[%s]", a.ptr, a.length); // Reset the length but keep the allocation a.length = 0; i = a.length; a~="1234"; writefln("%s[%s]", a.ptr, a.length); b~=a[i..$]; i = a.length; a~="&&"; writefln("%s[%s]", a.ptr, a.length); b~=a[i..$]; i = a.length; a~="asdf"; writefln("%s[%s]", a.ptr, a.length); b~=a[i..$]; writefln(join(b," ")); } -- Derek Parnell
Dec 05 2006