# struct

Definition: | [global] struct identifier ( "<c-string>" | { field [ ',' ... ] } ) end struct -- or -- struct|identifier variable ( ';' | '=' ( new() | expr ) ) [ ',' ... ] -- or -- bool res = struct|identifier(object o)
Description: | As well as defining new structs, the base struct builtin type can also be used, along with the identifiers it introduces, to declare and instantiate variables of that type, and/or test whether or not some value is of that type. Standard scope rules apply to identifier, specifically prefix with `global` to make any types and/or variables visible in other files, whereas the individual field names can only be accessed via dot notation syntax (or builtins\structs.e). For interfacing to C, use a c-struct, ie define the contents using a C string. See technicalia for additional notes on the mandatory ';' for unassigned struct vars and the optional parameters of new().
pwa/p2js: | Not supported.
Example: |
    struct bridge
        string name = "london"
        integer completed = 1971,
                age = 48
    end struct
    bridge b = new(),
           t = new({"tower",1894,125})
    ?b.age
    --b.age = "oops" -- type error
In their simplest form, structures are just a collection of fields. If no default value is specified for a field, one will be provided automatically: 0/false/""/{}/NULL for atom/bool/string/sequence/struct|object respectively. When you declare `struct bridge` there is also an implicit `type bridge()` automatically declared on your behalf. If you wanted something nice and simple, that’s it. If you wanted something a bit more powerful and flexible, read on. Simple structs can in fact also be extended and embedded, as will be explained shortly.
Implementation: | DoStruct() in pmain.e (along with several other places in that file) maps the above syntax to routines in builtins\structs.e Note the latter is "dual use": the compiler itself uses the same routines to lookup what it has already compiled, and of course that same information is also used at run-time, in other words the compiler both calls routines in structs.e and emits the same exact calls to be invoked (again) at run-time, as well as a few more run-time only calls.
See Also: | builtins\struct.e:new(), class
Technicalia | Note that (in the middle case) should you want to declare a (lowest level builtin) struct variable at file-level without invoking new() or otherwise assigning it on declaration, the ';' (or a ',') is mandatory for struct, ditto class. In short, "struct s = null" and "struct s;" are fine, but "struct s" (as an attempted top-level variable declaration) is likely to trigger a "weird" error somewhere down the line, and of course the same applies to the keyword "class". No such problem in local declarations. Obviously without a terminating semicolon, comma, or assignment operator (either '=' or ':=') the compiler would treat attempts at such variable declarations as a brand new type definition (top case), and say "end struct expected", probably somewhat dis-jointedly. Thankfully there is no such need for derived types, or during routine parameter or local variable declarations, since it is not legal to define a new type(/struct/class) then anyway, besides in any case (eg) "bridge b" (which does not need said mandatory terminator) is almost always preferable to "struct s" (which does, should you be trying to declare a variable). Phew, that was an excessive amount of hard work to explain a probably fairly rare and pretty minor edge case of an occasional mandatory semicolon! The effective signature for instantiation/creation is new(integer|string id=routine_id("identifier"), sequence defaults={}). The new() in bridge b = new() is automatically augmented by the compiler to new(routine_id("bridge")) [equivalent to new("bridge")]. Aside: also, bridge [not followed by '('] is now equivalent to routine_id("bridge") anyway, so the ability to specify with either a routine_id or a string is less significant, except the latter may occasionally make debugging easier, in that (say) 984 is faster but not instantly recognisable, whereas "bridge" is a known and valid structure name, and also clearly (for instance) not an animal. If the compiler cannot deduce the "bridge" from context, it issues an error forcing the programmer to provide it explicitly. If as shown for 't' above, new() is (only) given a sequence of initial default values [assigned in numeric order], then an appropriate struct identifier is squidged in beforehand, again automatically/when possible. The compiler assumes an integer/string is an id, and a sequence is defaults, and therefore replacing eg `s = new(setup[idx])` with `integer/string/sequence si = setup[idx]; s = new(si)` may help, ie you may need to explicitly tell the compiler the exact type of a single parameter to new(), typically by the simple act of storing it in a local variable of one of those three types. [Should you pass new() two parameters, the compiler (front-end) assumes you know what you are doing!] Technically struct is abstract (see class) so you cannot say `struct s = new("struct")`, however you can say `struct b = new("bridge")`, although of course `bridge b = new()` is altogether just easier/nicer/safer.