Can we truly assert that an array returned from a function is always a true copy?
Posted by jpluimers on 2020/09/23
The answer is no: [WayBack] Hi all, Can we truly assert that an array returned from a function is always a new true copy and that this is guaranteed to not change in between compil… – Ugochukwu Mmaduekwe – Google+
From Primož book and comment:
Depending on how you create this array.
For example, the following code outputs 42, not 17.
program Project142;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;function Bypass(const a: TArray<integer>): TArray<integer>;
begin
Result := a;
end;var
a, b: TArray<integer>;begin
SetLength(a, 1);
a[0] := 17;
b := Bypass(a);
a[0] := 42;
Writeln(b[0]);
Readln;
end.You can use `SetLength(arr, Length(arr))` to make array unique. Changing Bypass to the following code would make the test program emit 17.
function Bypass(const a: TArray<integer>): TArray<integer>;
begin
Result := a;
SetLength(Result, Length(Result));
end;
–jeroen






Remy Lebeau said
I would not rely on SetLength() for this. Forcing an array’s refcount to 1 during a resize, even if to the same length, is an implementation detail, not a guaranteed contract.
In XE7 and later, the System unit has a public DynArrayUnique() function to ensure a dynamic array’s refcount is 1, similar to System.UniqueString() for strings, eg something like this (I’m not sure if this is 100% correct, as I don’t have the source on hand to look at):
Result := a;
DynArrayUnique(Pointer(Result), TypeInfo(Integer));
Alternatively, you can just use System.Copy() instead, eg:
Result := Copy(a, 0, Length(a));
Or simply:
Result := Copy(a);
Either way would make the code more self-documenting that a copy of the source dynamic array is begin made explicitly, rather than SetLength() making a copy implicitly.