Ok, most of the troubles with Ada’s String
-type comes from a simple misunderstanding of array
-types in Ada, namely unconstrained arrays. (I go over it here, explicitly on Strings, but will reiterate; hopefully in a more understandable manner.)
Ok, to start, let us make a few types for illustration:
Type Elementary is range 32..64;
Type Indexia is range 0..12;
Subtype Positivity is Indexia'Succ(Indexia'First)..Indexia'Last;
Type Vectoria is array (Positivity range <>) of Elementary;
Subtype Fixedia is Vectoria( 8..11 );
So, here we have Vectoria
, an array indexed by Indexia
, but which doesn’t have any particular length associated with the type… Fixedia
, on the other hand, does have an inherent length: the range of 8 through 11, or a length of 4.
Now, because Vectoria
does not have a particular length, we don’t know how much space it takes up, so we cannot say X : Vectoria;
… there are two ways to get around this: (a) specifying the index-range in the type-portion of the variable (X : Vectoria(4..7);
), or (b) specifying the initial value (X : Vectoria := (33, 48, 60, 55);
). — These options are constraining the indefinite nature of the type into something definite.
Once X
’s type is definite, it thus has the length associated with it, and this cannot change: so, given that X
has a length of 4 in both of those examples, you cannot say either X:= (44, 55, 33);
or X:= (64, 54, 44, 34);
for the simple reason that the length/indices do not match.
Now, a slight detour, we mention “slices” and “index-sliding” — in the (a) example above the indices are 4..7
, but the following is legal X:= Fixedia'(32,42,53,64);
, this is because the indices 8..11
of Fixedia
are ‘slid’ into the 4..7
that we specified. Indeed, given
Y : Constant Vectoria:= (1 => 64, 2 => 63, 3 => 62, 4 => 61,
5 => 60, 6 => 59, 7 => 58, 8 => 57,
9 => 56, 10 => 55, 11 => 54, 12 => 53);
we could say X:= Y(X'Range);
, which copies the values of the slice on 4..7
—(61,60,59,58)
— into X
. Likewise, we could say X:= Y(8..11);
, and [IIRC] we could say X:= Y(Fixedia'Range)
for 8..11
(because it is length 4), using both slicing and sliding to get the appropriate “window” of our table-of-values.
Now, onto String
— but String
is not special.
It is simply an array which has elements of a Character-type; which in Ada is specified as any enumeration which has in its definition a character-literal, an element surrounded by single quotes. (See here.) / The only “special” thing about a String
type is that you can enclose in double-quotes a sequence of said character-literals. So…
Type Silly_Character is (NUL, BELL, HT, 'A', 'B', DEL);
Type Silly_String is array(Positive range <>) of Silly_Character;
given the above, you can say K : Silly_String:= "AAAB";
, which is shorthand for K : Silly_String:= (1 => 'A', 2 => 'A', 3 => 'A', 4 => 'B')
. Now, to use the non-graphic characters, it forces us to use an explicit array-aggregate as in the example, or else use the &
operator, so something like J : Silly_String:= BELL & BELL & "BB" & NUL & 'A';
.
So, that is why a lot of newcomers to Ada get tripped up on Strings
: they forget that they are working with arrays, which are really quite versatile (albeit with some limitations) in Ada because they can be unconstrained.
Hope that helps.