resizeable arrays: T[new]

Walter Bright newshound1 at digitalmars.com
Mon Jun 4 10:40:01 PDT 2007


Frits van Bommel wrote:
> Walter Bright wrote:
>> Now, it turns out that it is very rare for a function to legitimately 
>> want to resize a buffer passed to it. So we finally hit on the idea of 
>> making a resizeable array a different type, say:
>>
>>    T[n]   a;  // static array
>>    T[]    b;  // dynamic array
>>    T[new] c;  // resizeable array
>>
>>    a.length = 6;   // error, static array
>>    b.length = 6;   // error, dynamic array is not resizeable
>>    c.length = 6;   // ok
>>    b = c;          // ok, implicit conversion of resizeable to dynamic
>>    c = b;          // error
>>    c = b[0..n];    // error
>>    b = c[0..n];    // ok, but watch out if c is later resized
>>    b = c[0..n].dup; // ok, no worries
>>    c = cast(T[new])b; // ok, but this will be deliberate
> 
> What about
>     c = a;
> ?

Good question. That'll be allowed. I have a couple misgivings about it, 
though.

> I have several places where I have functions that follow this pattern:
> ---
> T[] foo(<parameters>, T[] buffer = null) {
>     buffer.length = <something>;
>     <fill buffer>
>     return buffer;
> }
> ---
> These are intended to be usable with "buffer" being either a 
> stack-allocated static array or a heap-allocated dynamic array. If the 
> passed buffer is big enough a slice is returned, otherwise it gets 
> enlarged (and possibly reallocated on the heap).
> I guess with this proposed modification I'd want to accept and return a 
> "T[new]", but will that allow static arrays (stack-allocated) to be 
> passed in?

Yes.

> I'd hate to have to write two different functions, one for static arrays 
> and slices and one for resizable arrays... (and I'm not fond of the idea 
> of extra casts to make it work with a single function)
> 
> Also, I often write toString() members of objects (and other functions 
> returning strings) like this:
> ---
> char[] toString() {
>     auto ret = "prefix";
>     foreach (elt; children) {
>         ret ~= elt.toString();
>         // perhaps append a separator here
>     }
>     // if separators were appended, remove the last one here
>     ret ~= "postfix";
>     return ret;
> }
> ---
> Will these need to be changed? (probably a .dup after the "prefix" string)
> (Since string constants are also static arrays, this is the same basic 
> issue as above)

You'd need to rewrite it as:

---
string toString() {
     invariant(char)[new] ret = "prefix";
     foreach (elt; children) {
         ret ~= elt.toString();
         // perhaps append a separator here
     }
     // if separators were appended, remove the last one here
     ret ~= "postfix";
     return ret;
}
---
since the type of "prefix" will be invariant(char)[6];
You'll need to use "string" as a return type, because you'll potentially 
be returning a reference to an immutable string literal, and that must 
be reflected in the type. If you chose to do "prefix".dup, it could be 
written as:

---
string toString() {
     auto ret = "prefix".dup;
     foreach (elt; children) {
         ret ~= elt.toString();
         // perhaps append a separator here
     }
     // if separators were appended, remove the last one here
     ret ~= "postfix";
     return ret;
}
---

and the return type could be either string or char[]. The type of ret 
here will be char[new], as that is what .dup will return.

>> I think this will entail only very rare changes to user code, but will 
>> be a big improvement in type safety. Note that it will *still* 
>> possible for array resizing to affect other slices into the same 
>> array, but it's far, far less likely to happen inadvertently. The 
>> attractiveness of this solution is it nearly completely solves the 
>> problem with zero runtime cost.
> 
> I think I may need to make quite some changes actually, though the 
> changes required to the latter code sample I gave will be minimal.
> 
> 
> Another question, will the following be allowed:
> ---
> class Base {
>     abstract char[] foo();
> }
> 
> class Derived : Base {
>     override char[new] foo() { assert(0, "Not implemented yet"); }
> }
> ---
> ?

Yes, since char[new] can be implicitly converted to char[]. The reverse 
will not work.



More information about the Digitalmars-d-announce mailing list