Aggregate Literals
Aggregate literals cover arrays and structs.
Array Literals#
Array literals construct fixed-size array values from a list of elements.
Surface Syntax#
An array literal is written using square brackets:
let xs = [1, 2, 3];
let ys = [1, 2, 3,]; // trailing comma allowed
Empty array literals are permitted only when an expected array type is available from context (so the compiler knows the element type and, for fixed-size arrays, the required length):
let empty: i32[0] = [];
let empty_slice: i32[] = [];
Typing#
- A non-empty array literal has type
T[N]whereNis the number of elements andTis inferred from the elements (or from an expected type when present). - When an expected type is present and it is
T[N], the literal must contain exactlyNelements. - When an expected type is present and it is
T[], the literal’s elements are type-checked againstTand the resulting value has typeT[]. - In Silk currently, this slice form is lowered as a non-owning view over a compiler-generated backing array.
- Lifetime rules are not yet enforced for such slices; do not allow a slice derived from a stack-backed array literal to outlive the scope where it was created.
Compiler requirements:
- Infer element type when possible, or require explicit annotation where ambiguity exists.
- Validate that all elements are convertible to the target element type.
- Enforce current-subset restrictions on which element types are supported for array lowering/codegen (see types and structs impls layout).
Struct Literals#
Struct literals construct values of struct types by specifying field names and values.
Surface Syntax#
A struct literal may be written in two forms:
- An explicit struct literal begins with a struct type name followed by a brace-enclosed field initializer list.
- A contextual (inferred) struct literal omits the type name and consists only of the brace-enclosed field initializer list. This form is only valid when an expected struct type is available from context (for example a function argument position or an explicit type annotation).
An explicit struct literal looks like:
struct Pair {
a: int,
b: int,
}
fn make () -> Pair {
return Pair { a: 1, b: 2 };
}
Whitespace may appear between the type name and { in ordinary expression
contexts. The parser suppresses that non-adjacent form only at the immediate
boundary where an expression is followed by a statement block (for example the
condition or iterable expression before if, while, for, or match block
braces). Inside delimited subexpressions such as array literals, call
arguments, indexes, parenthesized expressions, and accepted struct initializer
values, Type { ... } remains an explicit struct literal because the brace
cannot be the following statement block.
An inferred struct literal looks like:
struct User {
name: string,
}
fn print_user (user: User) -> void {
std::println("user.name = {}", user.name);
}
fn main () -> int {
// Equivalent to: `print_user(User{ name: "user name" });`
print_user({ name: "user name" });
return 0;
}
Initializers are written as either:
field_name: <expr>(explicit initializer), orfield_name(shorthand initializer, equivalent tofield_name: field_name).
Initializers are separated by commas and an optional trailing comma is permitted.
Example (shorthand):
struct User {
name: string,
}
fn main () -> int {
let name: string = "alice";
let user = User{ name }; // equivalent to `User{ name: name }`
if (user.name != "alice") { return 1; }
return 0;
}
Field defaults (struct declarations)#
A struct field declaration may include a default value expression:
struct Beep {
value: string = "boop",
}
When a struct literal omits a field, the compiler uses the field default expression when present; otherwise it falls back to zero-initialization in the current backend subset. This means the empty literal form is useful when all fields have defaults:
let b = Beep {};
Important notes:
- Inferred struct literals are a value construction mechanism. They do not
imply heap allocation. The compiler will not infer
&Tfrom{ ... }; usenewfor heap allocation explicitly. - The parser only treats
{ ... }as an inferred struct literal when it contains a struct-style initializer list (or is{}); blocks ({ Stmt* }) remain statement syntax (there is no general “block expression” in the current subset).
Default constructors (empty struct literals)#
If a struct defines a default constructor method with the signature:
fn constructor (mut self: &Self) -> void { ... }
then an empty struct literal invokes it as part of value construction:
Type{}(explicit empty literal){}when a struct type is expected from context (inferred empty literal)
Construction order:
- All struct slots are zero-initialized.
- Field default expressions are evaluated for omitted fields (if present).
- The default constructor is invoked, allowing it to mutate
self.
Visibility rule:
- The default constructor is invoked only when it is visible from the current
package (constructors are
publicby default; an explicitlyprivateconstructor is not invoked implicitly).
Supported forms note:
- Non-empty struct literals (for example
Type{ x: 1 }) do not invoke constructors implicitly.
Compiler requirements:
- Enforce that field names are valid and that each field is initialized at most once.
- Define the behavior for omitted fields (in the Supported forms, omitted fields are default-initialized).
- Respect struct lowering/layout rules from
structs-impls-layout.md.
Notes#
The current compiler implementation supports struct literals only for the
limited struct subset described in structs-impls-layout.md:
- structs with 0+ fields of supported value types (scalar primitives,
string, nested structs, and supported optionals), - literals may omit fields:
- omitted fields that have a field default (
field: T = <expr>) use that default expression, - otherwise, omitted fields are zero-initialized in the current backend subset,
- no duplicate field initializers are permitted,
- field order is not semantically significant.
Source repository · Edit this page · View Markdown