Ada: Strings

Ada does not have one string type — it has three, each with different ownership and performance rules: fixed String, Bounded_String, and Unbounded_String. Choosing among them deliberately is a core Ada skill.

Fixed String

String is an array of Character with a length fixed at declaration. It needs no library, no allocation, and is what every API ultimately speaks:

with Ada.Text_IO; use Ada.Text_IO;

procedure Fixed_Demo is
   Name : String (1 .. 10);            -- exactly 10 characters, index 1..10
   Greeting : constant String := "Hi, Ada";   -- length 7, known at compile time
begin
   --  Name := "Ada";         -- compile error: length must be exactly 10
   Name (1 .. 3) := "Ada";  -- a SLICE assignment: replace characters 1..3
   Name (4 .. 10) := "      ";         -- the rest stays blank padding
   Put_Line (Name (1 .. 3));           -- slice read: "Ada"
   Put_Line (Greeting'Length'Image);   -- 7 — attribute, no strlen() anywhere
end Fixed_Demo;

Fixed length sounds restrictive; it is actually a guarantee. A function taking String knows every caller's buffer exactly, there is no hidden allocation, and no NUL terminator — the length travels with the value.

Unbounded String

Unbounded_String (in Ada.Strings.Unbounded) grows and shrinks at run time. Assignment copies; the implementation manages the memory:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

procedure Unbounded_Demo is
   Line : Unbounded_String := To_Unbounded_String ("Hello");
begin
   Append (Line, ", ");                -- grows: now "Hello, "
   Append (Line, "world!");            -- "Hello, world!"
   Put_Line (To_String (Line));
   Put_Line (Natural'Image (Length (Line)));   -- 13
   --  Line (1) works too: unbounded strings support the same indexing
end Unbounded_Demo;

Use unbounded strings when text is user input, file content, or anything whose size you cannot predict. Each assignment may allocate — keep them out of tight loops.

Choosing the Right Type

TypeLengthAllocationUse when
StringFixed at declarationNone — it is an arrayFormat strings, keys, small buffers, APIs
Bounded_StringMax fixed at declarationNone beyond the objectVariable text with a hard cap; embedded code
Unbounded_StringDynamicHeap, managedUser input, file data, building text

Bounded_String is the compromise most languages never offer: dynamic-length semantics with statically guaranteed worst-case memory — one reason Ada runs on avionics hardware where heap use is banned.

Everyday String Operations

Concatenation (&), comparison (=, <, …), slicing, and translation cover daily needs. String literals have no type until context fixes one, so the same "Ada" can initialize any string-ish type:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

procedure Operations_Demo is
   Sentence : constant String  := "Ada is precise";
   Word     : constant String  := Sentence (1 .. 3);      -- "Ada"
   Upper    : constant Unbounded_String :=
      To_Unbounded_String (Sentence);
begin
   if Word = "Ada" then                       -- value comparison, not pointer
      Put_Line ("matched: " & Word);
   end if;
   Put_Line (Sentence & " by design.");       -- & concatenates any strings
   Put_Line (To_String (Upper));
   --  Ada.Strings.Fixed offers Count, Trim, Replace_Slice, Translate...
   --  Ada.Strings.Maps offers character sets for searching and filtering.
end Operations_Demo;
Common pitfall. A fixed String of length 5 cannot hold "Ada" (length 3) — but you can write Name := "Ada" & " " or use a slice. The compiler rejects length mismatches instead of corrupting memory, which is the entire point.

Next: Enumerations.