Values and types
Every binding has a type. let binds a name once, var allows reassignment.
fn main(): int {
let name = "Ada"; // string, inferred
let count: int = 3; // explicit
var total = 0; // reassignable
total = total + count;
return total;
}
let binds the name, not the contents. A list behind a let can still be modified; the name
just cannot be pointed at something else.
Primitive types
| Type | Width | Notes |
|---|---|---|
int |
64 bit, signed | the default; same width as int64, a distinct type |
uint |
64 bit, unsigned | the default; same width as uint64, a distinct type |
float |
64 bit | the default; same width as float64, a distinct type |
int8 … int64 |
sized | |
uint8 … uint64 |
sized | |
float32, float64 |
sized | |
bool |
true, false |
|
char |
one Unicode code point | 'a', '\u{1F600}' |
string |
immutable UTF-8 |
Numeric literals take a suffix when the default is not wanted: 7i32, 9u8, 1.5f32.
Distinct means distinct: a literal adapts to the annotated type (let n: int64 = 7;
works), but a variable of int does not assign to int64 or back — the widths agree
and the types still differ, and as is the crossing, as for any other pair.
Conversion
There is no implicit numeric conversion. as converts between numeric types and nothing else:
fn main(): int {
let small: int32 = 7i32;
let wide: int = small as int;
let back = wide as int32;
return back as int;
}
Strings
Strings are immutable. + concatenates, * repeats.
import std.io.console { println };
fn main(): int {
let greeting = "Hello, " + "world";
let line = "-" * 20;
println(greeting);
println(line);
return 0;
}
An f-string interpolates expressions. A format specifier follows a colon:
import std.io.console { println };
fn main(): int {
let pi = 3.14159;
println(f"pi is roughly {pi:N2}");
println(f"{1 + 1} and {"nested" + "!"}");
return 0;
}
Write {{ and }} for a literal brace.
An interpolation holds a SCALAR — a number, a bool, a char or a string. A struct, a
class or an enum is refused there even when it conforms to Display, so a type that prints
through println still needs .show() inside an f-string:
import std.io.console { println };
import std.time { Instant };
fn main(): int {
let at = Instant.ofEpochMillis(1000);
println(at); // Display, through the constraint
println(f"at {at.show()}"); // f"{at}" is refused
return 0;
}
Arrays
T[] is a fixed-length array. Its length is part of the value, not of the type.
import std.io.console { println };
fn main(): int {
let numbers = [3, 7, 1];
let zeros = [0] * 5;
let both = numbers + zeros;
println(f"{numbers[0]} of {numbers.length}");
return both.length;
}
Arrays do not grow. std.collections has List<T> for that.
Tuples
A tuple groups values of different types. It has no field names; you take it apart by binding.
fn divide(a: int, b: int): (int, int) {
return (a / b, a % b);
}
fn main(): int {
let (quotient, remainder) = divide(17, 5);
return quotient + remainder;
}