I've recently discovered an interesting "gotcha" regarding dynamic arrays in Delphi and was wondering of the best way to avoid the issue.
Let's say we have the following example where a dynamic array variable is reused:
function FillArray(Count: Integer): TArray<Integer>;
var
i: Integer;
begin
SetLength(result, Count);
for i := 0 to Count - 1 do
result[i] := i;
end;
procedure TfrmMain.Button1Click(Sender: TObject);
var
list: TArray<Integer>;
begin
list := FillArray(5);
meLog.Lines.Add(IntToStr(NativeInt(list)));
list := FillArray(8);
meLog.Lines.Add(IntToStr(NativeInt(list)));
list := FillArray(12);
meLog.Lines.Add(IntToStr(NativeInt(list)));
end;
As a function's result variable is just an implict var parameter the list variable is being reused and as the subsequent size of the array is being increased, 5 to 8 and then to 12, then the array is being reallocated, thus the output is:
2138845992
2138930232
2138887416
But if I start with creating with a size of 12 first:
list := FillArray(12);
meLog.Lines.Add(IntToStr(NativeInt(list)));
list := FillArray(8);
meLog.Lines.Add(IntToStr(NativeInt(list)));
list := FillArray(5);
meLog.Lines.Add(IntToStr(NativeInt(list)));
then the same dynamic array is reused, as no reallocation is needed, the output is then:
2138887416
2138887416
2138887416
This is a nasty "gotcha" as if I store each assignment of list somewhere else then I am not getting unique arrays.
This is easy to avoid, I can either do:
function FillArray(Count: Integer): TArray<Integer>;
var
i: Integer;
begin
result := nil;
SetLength(result, Count);
for i := 0 to Count - 1 do
result[i] := i;
end;
or
list := nil;
list := FillArray(12);
meLog.Lines.Add(IntToStr(NativeInt(list)));
list := nil;
list := FillArray(8);
meLog.Lines.Add(IntToStr(NativeInt(list)));
list := nil;
list := FillArray(5);
meLog.Lines.Add(IntToStr(NativeInt(list)));
or
list := Copy(FillArray(12));
meLog.Lines.Add(IntToStr(NativeInt(list)));
list := Copy(FillArray(8));
meLog.Lines.Add(IntToStr(NativeInt(list)));
list := Copy(FillArray(5));
meLog.Lines.Add(IntToStr(NativeInt(list)));
any of these will give me a unique array. The best seems to be result := nil, as you would assume such a function should return a unique array. But setting result := nil, then doing a setLength just looks wrong and someone not understanding the issue may remove the result := nil in the future thinking it is redundant.
So my question is, is my understanding of this correct and is there a better way to create a unique dynamic array?