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
| Type | Length | Allocation | Use when |
|---|---|---|---|
String | Fixed at declaration | None — it is an array | Format strings, keys, small buffers, APIs |
Bounded_String | Max fixed at declaration | None beyond the object | Variable text with a hard cap; embedded code |
Unbounded_String | Dynamic | Heap, managed | User 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;
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.