Structs, enums and patterns
Structs
A struct groups named fields. Fields end with ;. Functions declared inside are its methods (with a this receiver) or static functions (without one).
struct Point {
x: i64;
y: i64;
fn origin() -> Point => Point { x: 0, y: 0 }; // static: Point.origin()
fn manhattan(this) -> i64 { // `this` by value (Point is Copy)
return abs(this.x) + abs(this.y);
}
fn moveBy(borrow mut this, dx: i64, dy: i64) { // changes the point in place
this.x += dx;
this.y += dy;
}
}
fn abs(v: i64) -> i64 => if (v < 0) -v else v;
fn main() {
var p = Point.origin();
p.moveBy(3, -4);
Console.writeLine(p.manhattan());
let q = Point { x: 1, y: 2 }; // every field, in any order
Console.writeLine(q.x + q.y);
}7
3- A struct literal names every field exactly once.
- The receiver says how a method gets its struct:
this(a copy, or a move),borrow this(read-only access) orborrow mut this(changes the caller's value). Calling aborrow mut thismethod needs avar(or a mutable reference); the call borrows it for the duration of the call. - A struct is Copy, copied on assignment, when every field is Copy and it has no
dropblock. Otherwise it moves: see Ownership. - Fields are laid out in declaration order with C's rules, so a struct is the same in C.
Visibility
Items and members are internal by default: visible throughout their package. public makes them visible to other packages (see Packages); private on a field limits it to the file that declares the struct, which lets a type guard an invariant.
Enums
An enum is one of several variants, each with zero or more positional payloads. Variants are written with the enum's name.
enum Shape {
Empty,
Circle(i64),
Rect(i64, i64),
}
fn area(s: Shape) -> i64 => match (s) {
Shape.Empty => 0,
Shape.Circle(r) => 3 * r * r,
Shape.Rect(w, h) => w * h,
};
fn main() {
Console.writeLine(area(Shape.Circle(2)));
Console.writeLine(area(Shape.Rect(3, 4)));
Console.writeLine(area(Shape.Empty));
}12
12
0match
match tries its arms in order. It must be exhaustive: every possible value matches some arm, or the compiler names one that does not. An arm that can never be reached is an error too.
As a statement, each arm is a block; as an expression (above), each arm is a value, separated by commas.
enum Command {
Move(i32, i32),
Wait(u32),
Quit,
}
fn run(command: Command) {
match (command) {
Command.Move(0, 0) => { Console.writeLine("stay"); }
Command.Move(x, _) => {
Console.write("move x by ");
Console.writeLine(x);
}
Command.Wait(seconds) => {
Console.write("wait ");
Console.writeLine(seconds);
}
Command.Quit => {}
}
}
fn main() {
run(Command.Move(0, 0));
run(Command.Move(5, 1));
run(Command.Wait(3));
run(Command.Quit);
}stay
move x by 5
wait 3Patterns can be nested: a variant inside a variant (Ok(Shape.Circle(r))), a literal in a payload's position (Command.Move(0, 0)), null or Some(…) for optionals, and _ for anything. Integers and booleans can be matched directly, with _ for the rest.
fn kind(n: u8) -> StringView => match (n) {
0 => "zero",
'a' => "the letter a",
_ => "something else",
};
fn main() {
Console.writeLine(kind(97));
Console.writeLine(kind(3));
}the letter a
something elseOptionals
T? is a T or null. A plain T converts to T? wherever one is expected.
fn find(values: Slice<i32>, wanted: i32) -> usize? {
for (i in 0..values.length) {
if (values[i] == wanted) {
return i; // converts to usize?
}
}
return null;
}
fn firstOfTwo(values: Slice<i32>) -> usize? {
let a = find(values, 2)?; // `?`: if null, return null
return a;
}
fn main() {
let numbers = [5, 2, 9];
match (find(numbers[..], 9)) {
Some(i) => { Console.writeLine(i); }
null => { Console.writeLine("absent"); }
}
Console.writeLine(find(numbers[..], 7) == null);
Console.writeLine(firstOfTwo(numbers[..])!); // `!`: the value, or stop the program if null
}2
true
1o == nullando != nulltest for absence.o!is the value; onnullit stops the program with "unwrapped null".o?is the value; onnullthe enclosing function returnsnull(it must return an optional).- A reference can be optional (
borrow T?), and so can a box (Box<T>?); both are one pointer.
Results
Result<T, E> is the other built-in enum: Ok(value) or Err(error). It is how functions report failure; see Errors.
Next
Ownership and borrowing: what happens when a value is assigned, passed and destroyed.