Language tour
Functions, variables, loops, arrays, structs, borrows and panics, each with what it prints.
Each program here runs as shown with peeper run; the box under it is what
it prints. The compiler repository's examples/ folder has a longer program
for each part.
Functions and constants
A function takes typed parameters and may give a result. The program starts
at main; an i32 result is its exit code. A constant's value is computed
while compiling, and its type can come from where it is used.
const Limit = 10; // its type comes from where it is used
const Answer: i32 = Limit * 4 + 2; // computed while compiling
fn square(x: i64) -> i64 {
return x * x;
}
fn gcd(a: i64, b: i64) -> i64 {
if b == 0 {
return a;
}
return gcd(b, a % b);
}
fn main() -> i32 {
println("square:", square(Limit));
println("gcd:", gcd(48, 18));
return Answer - 42; // the exit code: 0
}square: 100
gcd: 6Variables
let names a value and mut lets it change. A let without a value is set
by its first assignment, and the compiler checks it is set once on every
path before it is read.
fn sign(value: i32) -> i32 {
let result: i32; // set once on every path below
if value > 0 {
result = 1;
} else if value < 0 {
result = -1;
} else {
result = 0;
}
return result;
}
fn main() {
let fixed = 5; // `i32`, from the value
let mut total: i64 = 0;
total = total + fixed;
println("total:", total, "signs:", sign(-8), sign(0), sign(3));
}total: 5 signs: -1 0 1Loops and ranges
for is the only loop. It goes over a range, an array or a slice, runs while
a condition holds, or runs until break. a..b counts up to b, a..=b
takes b too, :step steps by more than 1, and a range counts down when
a > b.
fn main() {
print("step:");
for v in 0..10:3 { print("", v); }
println();
print("down:");
for v in 5..=0:2 { print("", v); }
println();
let mut n = 1;
for n < 100 { // while `n < 100`
n = n * 3;
}
println("while:", n);
}step: 0 3 6 9
down: 5 3 1
while: 243Arrays and references
[N]T holds N values of type T, and .len is N. An array copies when
assigned or passed. & takes a reference, &mut one that can change the
value, and .* reaches the value. values... spreads one array's values
into another.
fn bump(values: &mut [3]i32) { // changes the caller's array
for value in values {
value.* = value.* + 1;
}
}
fn main() {
let a = [3]i32{1, 2, 3};
let mut c = a; // a copy: `a` stays as it is
c[0] = 9;
bump(&mut c);
println("a:", a, "c:", c, "len:", c.len);
println("spread:", [_]i32{a..., 0, a...});
}a: [1, 2, 3] c: [10, 3, 4] len: 3
spread: [1, 2, 3, 0, 1, 2, 3]Structs and moves
Name.{ field = value } makes a struct, with the fields in any order, and
.{ ... } takes the type from where it goes. A struct moves when assigned or
passed: the old variable is then unset.
struct Point { x: i32, y: i32 }
struct Line { start: Point, end: Point }
fn length(line: Line) -> i32 { // takes the line, moved in
return line.end.x - line.start.x + line.end.y - line.start.y;
}
fn main() {
let start = Point.{ y = 2, x = 1 };
let end: Point = .{ x = 4, y = 6 };
let line = Line.{ start = start, end = end };
println("line:", line);
println("length:", length(line)); // `line` moves; it is unset now
}line: Line { start: Point { x: 1, y: 2 }, end: Point { x: 4, y: 6 } }
length: 7Borrows
A &mut borrow may not overlap a reference that is still in use. The
compiler names all three places: where the reference was made, where the
&mut borrow starts, and where the reference is used later.
fn update(value: &mut i32, by: i32) {
value.* = value.* + by;
}
fn main() {
let mut count = 1;
let view = &count;
update(&mut count, 2);
println("count:", view.*);
}[T0037]: `count` is borrowed as `&mut` while a reference to it is still in use --> src/main.peep:8:12 | 6 | let mut count = 1;7 | let view = &count; | ------ reference made here8 | update(&mut count, 2); | ^^^^^^^^^^ borrowed as `&mut` here9 | println("count:", view.*); | ------ reference used later here | = note: a `&mut` argument takes hold when the call startsCompilation failed with 1 error(s)Checks while running
What the compiler cannot know, the program checks when it runs: an index against the length, a division by zero, a shift past the width. A failed check stops the program with exit code 101 and says where.
fn main() {
let values = [3]i32{4, 8, 15};
for i in 0..4 {
println("value:", values[i]);
}
}value: 4
value: 8
value: 15
panic: index out of bounds: the length is 3
--> src/main.peep:4:27Next
Each part of the language has its own page, with a whole program and what it prints. Start with Functions and constants.