Structs, tuples and arrays.
Structs, tuples and arrays are Silk's product types. This page covers struct and tuple declarations, record and tuple literals, anonymous aggregates, fixed arrays, and slices.
Syntax#
StructDecl = 'pub'? 'struct' Name TypeParams? '{' Field* '}'
Field = 'pub'? Name ':' Type
TupleDecl = 'pub'? 'tuple' Name TypeParams? '(' ( Type ( ',' Type )* ','? )? ')'
RecordLiteral = TypePath '{' ( FieldInit ( ',' FieldInit )* ','? )? '}'
ContextRecord = '.' '{' ( FieldInit ( ',' FieldInit )* ','? )? '}'
FieldInit = Name ':' Expression
TupleLiteral = '(' Expression ',' ( Expression ( ',' Expression )* ','? )? ')'
UnitLiteral = '(' ')'
ArrayLiteral = '[' ( Expression ( ',' Expression )* ','? )? ']'
ArrayRepeat = '[' Expression ';' Expression ']'
FieldAccess = Expression '.' Name
ElementAccess = Expression '.' DecimalInteger
IndexAccess = Expression '[' Expression ']'
ArrayType = '[' Type ';' Expression ']'
SliceType = '&' 'mut'? '[' Type ']'
UnitType = '(' ')'Struct declarations#
Fields are separated by commas, usually one per line. A trailing comma is allowed. A missing comma is PAR0001, "Expected
,".A field name may appear once (SEM0017).
A struct may be empty:
struct Marker {}.extern "C" struct Name { }declares a C-layout record. It takes no type parameters (SEM0205), and its fields must be C object types (SEM0206). See Unsafe code and C interop.Generic structs take type parameters:
struct Labeled<T> { label: u8, value: T }. See Generics.
Visibility#
pub structmakes the type nameable from other modules. Each field is private unless markedpub.Reading or assigning a private field from another module is SEM0028.
A record literal from another module needs every field to be visible. Otherwise construction is SEM0021; offer a
pub fnconstructor instead.Importing a non-
pubstruct from another module is SEM0015.Elements of a
pub tupleare accessible from other modules.
Record literals#
Name { a: 1, b: 2 }constructs a struct. Every field is required (SEM0024). Fields may appear in any order.Initializers are separated by commas. A trailing comma is allowed.
Each initializer needs
name: value. There is no field shorthand:Point { x, y: 2 }is a parse error.There is no struct update syntax (
..base).An unknown field is SEM0022. A repeated field is SEM0023. A value of the wrong type is SEM0025.
Generic arguments may be written or inferred:
Labeled<Point> { … }orLabeled { … }.In an
iforwhilecondition and amatchsubject, a record literal must be in parentheses.
Contextual and anonymous records#
.{ a: 1, b: 2 }takes its struct type from context: an annotation, a parameter, a return type, or amatcharm with an expected type.Without a struct context,
.{ … }creates an anonymous record. Its fields are read with.name.Each anonymous literal has its own type. Assigning a second
.{ only: 2 }to a variable that holds.{ only: 1 }is SEM0037.An anonymous record passed to a generic parameter binds the parameter to that anonymous type.
Using
.{ }where a tuple is expected, or( , )where a struct is expected, is SEM0173.
Field access and assignment#
value.fieldreads a field. It works through references too:p.xwherep: &Point.value.field = exprwrites a field. The root binding must belet mut(SEM0035), or the path must go through a&mutreference.Nested paths work:
line.end.value = 5.A missing field is SEM0027. Projecting a field from a non-struct is SEM0026.
Structs are not
Copyby default.let b = aneedsmove a(OWN0003). See Ownership and moves.
Tuples#
tuple Name(A, B)declares a nominal tuple. Construct it like a call:Name(a, b). The wrong number of elements is SEM0172, which names the tuple: "main.Pair expects 2 tuple elements but received 3".A literal
(a, b)also constructs a nominal tuple when the expected type is one:let r: Range = (0, 1).Elements are read and written with
.0,.1and so on. An out-of-range ordinal is SEM0027. For a nominal tuple it lists the elements: "main.Pair has no element.2; it has 2 elements,.0and.1".Name { … }record syntax does not construct a tuple.Pair { 0: a, 1: b }is PAR0010: "A tuple's elements have no field names; build it by position, as inPair(first, second)". Other field names are SEM0018.A
(a, b)literal with no tuple context creates an anonymous tuple. Like anonymous records, each literal is its own type.There is no tuple type syntax.
(T)in a type is justTin parentheses, so(i32, bool)is not a type. Declare atupleto name one.(x)is a grouped expression. A one-element tuple literal needs a trailing comma:(x,).
Unit#
()is both the unit type and its only value.A function with no result type returns
().
Fixed arrays#
[T; N]is an array of exactlyNelements.Nmay be a literal or a constant:[i32; N].[a, b, c]is an array literal. All elements must have one type (SEM0030). Against an annotation, the count must match (SEM0031).With no expected type, elements whose types differ only in a lifetime join at the shortest one:
[header("a", b"x"), header("b", &local)]is valid and lives as long aslocal. Using it past that is OWN0019.[value; count]repeats one value:[0; 64],[[7; 3]; 2].countis a static integer, such as a literal, aconst, alet staticbinding or astaticparameter. A runtime count is SEM0176.With a
countabove 1, the value's type must beCopy(SEM0121). Against an annotation,countmust match the length (SEM0031).[]needs an expected array type:let none: [u8; 0] = []. Otherwise SEM0029.Arrays nest:
[[1, 2], [3, 4]], read withgrid[1][0]..lengthis the element count asusize. There is no.len:a.lenis SEM0027, "[i32; 3] has no fieldlen; its element count is.length", and the same for a slice.An array of
Copyelements is copied on assignment. An array with non-Copyelements needsmove.
Indexing#
a[i]reads an element anda[i] = vwrites one. The index must beusize(SEM0033). An unannotated literal variable such aslet i = 1isi32, so annotate it.[must be on the same line as the indexed expression. On a new line it starts a new statement.A constant index outside a fixed array's length is rejected at compile time (SEM0034).
Any other out-of-range index traps at run time with
fatal trap: index out of bounds. See Program entry and termination.Indexing a value that is not an array or slice is SEM0032.
Slices#
&[T]is a shared view of contiguous elements.&mut [T]is an exclusive view that allows writes.Borrow an array to get a slice:
&aor&mut a, passed where&[T]or&mut [T]is expected. Passingaitself is a type error (SEM0012).&[T; N]is a reference to a whole fixed array, keeping its length in the type.Slices support
.length, indexing and element assignment (for&mut [T]).There is no range or slicing syntax such as
a[1..3]. UseSlice.view(values, offset, length)fromsilk/collections/slicefor a shared subrange, andSlice.viewMutfor an exclusive one. An invalid range traps withslice range out of bounds.See Borrowing and Lifetimes for how long a slice may live.
Examples#
pub struct Point {
pub x: i32,
pub y: i32,
}
struct Labeled<T> {
label: u8,
value: T,
}
fn origin() -> Point {
return .{ x: 0, y: 0 }
}
fn lengthSquared(p: &Point) -> i32 {
return p.x * p.x + p.y * p.y
}
pub fn main() -> i32 {
let mut p = Point { y: 4, x: 3 }
if lengthSquared(&p) != 25 {
return 1
}
p.x = 0
let o = origin()
if lengthSquared(&p) != 16 || o.y != 0 {
return 2
}
let boxed = Labeled<Point> { label: 1, value: move p }
let inferred = Labeled { label: 2, value: true }
if boxed.value.y != 4 || !inferred.value {
return 3
}
return 0
}tuple Meters(f64)
tuple Range(i32, i32)
fn width(r: &Range) -> i32 {
return r.1 - r.0
}
fn unit() -> Range {
return (0, 1)
}
pub fn main() -> i32 {
let mut r = Range(2, 9)
r.0 = 3
let m = Meters(1.5)
let pair = (10, true)
let nothing: () = ()
if width(&r) != 6 || width(&unit()) != 1 {
return 1
}
if m.0 != 1.5 || !pair.1 || pair.0 != 10 {
return 2
}
return 0
}import silk.collections.slice { Slice }
fn total(values: &[i32]) -> i32 {
let mut sum = 0
let mut index: usize = 0
while index < values.length {
sum = sum + values[index]
index = index + 1
}
return sum
}
fn clear(values: &mut [i32]) -> () {
let mut index: usize = 0
while index < values.length {
values[index] = 0
index = index + 1
}
}
pub fn main() -> i32 {
let mut numbers: [i32; 5] = [1, 2, 3, 4, 5]
numbers[0] = 10
let grid = [[1, 2], [3, 4]]
let none: [u8; 0] = []
if total(&numbers) != 24 || grid[1][0] != 3 || none.length != 0 {
return 1
}
let middle = Slice.view(&numbers, 1, 3)
if middle.length != 3 || total(middle) != 9 {
return 2
}
clear(&mut numbers)
return total(&numbers)
}Diagnostics#
| Code | Meaning |
|---|---|
| PAR0001 | Missing token, for example no comma between struct fields ("Expected ,") |
| PAR0010 | Tuple literal with field names, Pair { 0: a, 1: b } |
| SEM0012 | Argument type mismatch, for example an array passed where a slice is expected |
| SEM0015 | Import of a private member of another module |
| SEM0017 | Struct declares a field twice |
| SEM0021 | Struct cannot be constructed here because a field is not visible |
| SEM0022 | Record literal names an unknown field |
| SEM0023 | Record literal initializes a field twice |
| SEM0024 | Record literal omits a field |
| SEM0025 | Record literal field value has the wrong type |
| SEM0026 | Field projection on a value that is not a struct |
| SEM0027 | Unknown field or tuple element, including .len for .length |
| SEM0028 | Private field of another module's struct |
| SEM0029 | Empty array literal without an expected array type |
| SEM0030 | Array element has the wrong type |
| SEM0031 | Array literal or repeat length differs from the expected length |
| SEM0032 | Indexing a value that is not an array or slice |
| SEM0033 | Index is not usize |
| SEM0034 | Constant index outside the array's length |
| SEM0035 | Assignment through an immutable binding |
| SEM0037 | Assigned value has the wrong type, including a different anonymous aggregate |
| SEM0121 | Repeated array value is not Copy |
| SEM0176 | Repeat count is not a static value |
| SEM0172 | Tuple constructed with the wrong number of elements |
| SEM0173 | Tuple literal used for a struct, or record literal used for a tuple |
| OWN0003 | Non-Copy value consumed without move |
See also#
Values and primitive types · Bindings and statements · Enums and unions · Patterns and match · Generics · Ownership and moves · Borrowing · Lifetimes · Unsafe code and C interop · Program entry and termination · silk/collections/slice, silk/collections/list
Open questions#
The catalog defines SEM0059 (array passed as a slice without a borrow) and SEM0175 (named-field construction of a positional aggregate), but the compiler has no producer for either. It reports SEM0012 ("Expected &[i32] but received [i32; 3]") and SEM0018 ("A type position whose name resolves to something other than a type") instead.