stringada

Cannot use `'First` and `'Last` on unbounded strings


When trying to get the first or last index of an unbounded string, as shown in this program

with Ada.Strings; use Ada.Strings; -- for `Backward`
with Ada.Strings.Unbounded;
with Ada.Text_IO; use Ada.Text_IO;

procedure Foo is
    package U_Str renames Ada.Strings.Unbounded;

    S : U_Str.Unbounded_String := U_Str.To_Unbounded_String ("example unbounded string");
    I : Natural := U_Str.Index (Source => S,
                                Pattern => "s",
                                Going => Backward,
                                From => S'Last);
begin
    Put_Line (I'Image);
end Foo;

GNAT complains about U_Str.Unbounded_String being a private type:

$ gnatmake foo.adb
x86_64-linux-gnu-gcc-10 -c foo.adb
foo.adb:12:41: prefix for "Last" attribute may not be private type
gnatmake: "foo.adb" compilation error

I don't understand this, the same program with a regular String works fine:

with Ada.Strings; use Ada.Strings; -- for `Backward`
with Ada.Strings.Fixed;
with Ada.Text_IO; use Ada.Text_IO;

procedure Foo is
    package F_Str renames Ada.Strings.Fixed;

    S : String := "example fixed string";
    I : Natural := F_Str.Index (Source => S,
                                Pattern => "s",
                                Going => Backward,
                                From => S'Last);
begin
    Put_Line (I'Image);
end Foo;
$ gnatmake foo.adb 
x86_64-linux-gnu-gcc-10 -c foo.adb
x86_64-linux-gnu-gnatbind-10 -x foo.ali
x86_64-linux-gnu-gnatlink-10 foo.ali
$ ./foo 
 15

Do unbounded strings have no 'First and 'Last attributes? The Ada Reference Manual only states

The following attributes are defined for a prefix A that is of an array type (after any implicit dereference), or denotes a constrained array subtype

but it does not say anything about private types. Is this a GNAT bug or did I miss something?


Solution

  • The attributes 'First and 'Last are indeed unavailable for private types. Instead, note that "Unbounded_String represents a String whose low bound is 1 and whose length can vary conceptually between 0 and Natural'Last." Moreover, "The function Length returns the length of the String represented by Source." As a result, you can use the Length function to search from the string's end.

    From => U_Str.Length(S));
    

    This yields the value 19 for I in your first example.