In Pascal there are two kinds of type declarations:
- type aliases: type NewName = OldType
- type creation: type NewType = type OldType
The former is just creating convenient shorthand, like typedef in C. The aliases are compatible one to another and to their original type. The created types are intentionally incompatible and cannot be mixed without explicit and unsafe by definition typecast.
var
nn: NewName; nt: NewType; ot: OldType;
...
nn := ot; // should work
nt := ot; // should break with type safety violation error.
nt := NewType(ot); // Disabling type safety. Should work even if
// it has no sense semantically and types really ARE incompatible.
Those are Pascal basics as i understand them.
Now let's look at one certain type and two its aliases:
- System.Types.TStringDynArray = array of string;
- System.TArray<T> = array of T;
- in particular that means TArray<string> = array of string; by definition.
Now let's take function returning the former type alias and feed its result to the function expecting the latter one:
uses Classes, IOUtils;
TStringList.Create.AddStrings(
TDirectory.GetFiles('c:\', '*.dll') );
TStringList.Create.AddStrings(
TArray<string>( // this is required by compiler - but why ???
TDirectory.GetFiles('c:\', '*.dll') ) );
1st snippet would not compile due to types violation. 2nd one happily compiles and works, but is fragile towards future type changes and is redundant.
QC tells that compiler is right and the RTL design is wrong. http://qc.embarcadero.com/wc/qcmain.aspx?d=106246
WHY compiler is right here ? Why those aliases are incompatible ? Even the very manner RTL was designed suggests that they were deemed compatible!
PS. David suggested even simplier example, without using TArray<T>
type T1 = array of string; T2 = array of string;
procedure TForm1.FormCreate(Sender: TObject);
function Generator: T1;
begin Result := T1.Create('xxx', 'yyy', 'zzz'); end;
procedure Consumer (const data: T2);
begin
with TStringList.Create do
try
AddStrings(data);
Self.Caption := CommaText;
finally
Free;
end;
end;
begin
Consumer(Generator);
end;
Same gotcha without explanation...
PPS. There are a number of doc refs now. I want to stress one thing: while this restriction might be indirectly inherited from Pascal Report of 1949, today is 2012 and Delphi used very differently from school labs of half-century ago. I named few BAD effects of keeping this restrictions, and yet did not saw any good one.
Ironic thing, that this restricion may be lifted without breaking rules of Pascal: in Pascal there is no such non-strict beast as Open Arrays and Dynamic Arrays. So let those original fixed arrays be restricted as they wish, but Open Arrays and Dynamic Arrays are not Pascal citizens and are not obliged to be limited by its codebook!
Please, communicate Emba in QC or maybe even here, but if u just pass by without expressing your opinion - nothing would change!
type T1 = array of string; T2 = array of string
and then attempt to assign a variable of type T1 to one of type T2. – David Heffernan