Question:
Function exchange(input: String): String;
Var
I: Integer;
Begin
Result:=input;
I:=Length(Result);
For I:=1 To I Do Begin
If Result[I]='a' Then Result[I]:='b'; //[DCC Error] test.pas(13): E1047 Unsafe code 'String index to var param'
If Result[I]='c' Then Result[I]:='d'; //[DCC Error] test.pas(14): E1047 Unsafe code 'String index to var param'
If Result[I]='e' Then Result[I]:='f'; //[DCC Error] test.pas(15): E1047 Unsafe code 'String index to var param'
End;
End;
It is to change that you can turn off warnings, I know, I'm wondering how this "reliable" code is written and what exactly does he not like here?
Answer:
Here is the code that will be correct and error-free:
Function exchange(input: String): String;
Var
I: Integer;
sb: TStringBuilder;
Begin
sb := TStringBuilder.Create(input);
try
For I := 0 To sb.Length - 1 Do
Begin
if sb.Chars[I] = 'a' Then sb.Chars[I] := 'b';
if sb.Chars[I] = 'c' Then sb.Chars[I] := 'd';
if sb.Chars[I] = 'e' Then sb.Chars[I] := 'f';
End;
Result := sb.ToString;
finally
sb.Free;
end;
End;
And now the explanation:
In modern versions of Delphi, when introducing support for new platforms (iOS, Android, x64, etc.), it was decided to move away from rows indexed 1 .. N
in favor of standard ones indexed 0 .. N-1
. This change breaks almost all existing code. Therefore, its implementation is very slow and gradual. In some version, warnings were added, in some {$IFDEF ZEROBASEDSTRINGS}
. Somewhere this directive is enabled by default, but mostly not.
What this warning says is that accessing rows by index is unsafe, because indexing may not be known in advance (depending on the directive and the target platform).
How to be?
- The simplest solution if you only need Win32 is to turn off the warnings and move on with your life.
- The most correct thing, if you need new platforms, is to rewrite the code using a string builder (
TStringBuilder
), which itself will figure out exactly how the string is indexed, and provides a single interface to it through its methods.
PS Oddly enough, For I:=1 To I Do
is quite correct code, because the loop keeps the maximum value before iteration starts.