Fields are not the only entity that can appear in the definition of a struct type.
Suppose that after reading more carefully the Packet Specification 1.2
(that spans for several thousand of pages) we realize that the field
size
doesn’t really store the number of bytes of the payload
and control arrays, like we thought initially. Or not exactly: the
Packet Foundation says that if size
has the special value 0xff,
then the size is zero.
We could of course do something like this:
type Packet = struct { byte magic = 0xab; byte size; byte[size == 0xff ? 0 : size] payload; byte[size == 0xff ? 0 : size] control; };
However, we can avoid replicating code by using a variable instead:
type Packet = struct { byte magic = 0xab; byte size; var real_size = (size == 0xff ? 0 : size); byte[real_size] payload; byte[real_size] control; };
Note how the variable can be used after it gets defined. In the underlying process of mapping or constructing the struct, the variable is incorporated into the lexical environment. Once defined, it can be used in constraint expressions, array sizes, and the like. We will see more about this later.
Incidentally, it is of course possible to use global variables as well. For example:
var packet_special = 0xff; type Packet = struct { byte magic = 0xab; byte size; var real_size = (size == packet_special ? 0 : size); byte[real_size] payload; byte[real_size] control; };
In this case, the global packet_special
gets captured in the
lexical environment of the struct type (in reality in the lexical
environment of the implicitly created mapper and constructor
functions) in a way that if you later modify packet_special
the
new value will be used when mapping/constructing new values of
type Packet. Which is really cool, but let’s not get distracted from
the main topic... :)