Reference / Functions
Functions
A function is a named block of code with parameters and an optional return type. A call of the function runs its body.
fn area(width: i64, height: i64) -> i64 {
return width * height
}
fn greet(name: string) {
print("Hello, {name}")
}
fn main() {
greet("Aino") // Hello, Aino
print(area(6, 7)) // 42
}
A call gives the arguments to the parameters in order (Calls).
Declarations
A function declaration is fn, the name, the parameters in parentheses, an optional return type after ->, and the body. A function is declared at the top level.
A function in the body of a class or an enum is a method (Classes). This page shows functions without type parameters. A generic function also has type parameters after its name (Generics).
fn main() {
print(double(21)) // 42
}
fn double(n: i64) -> i64 {
return n * 2
}
The rules:
- A function can call a function that is declared below it.
- Functions, classes, enums and constants share one set of names. Each name is declared once (E0034).
- A function cannot take the name of a built-in type, such as
i64, orOption,Result,Some,OkorErr(E0026). It cannot take the nameprint,eprint,panicorread_line(E0034). Input and output tells these functions. The sections dbg and Files are in development, and they change this rule. - A function cannot be named
_(E0080). - A function cannot be declared in the body of another function.
- A function that no live code calls builds with a warning (W0012). Live code is
main, each function, method and constant whose name starts with_, and all the code that live code calls or names. - A binding or a parameter can take the name of a function or a class. A call still finds the function or the class, because a binding is never called (Shadowing).
fn twice() {
print(1)
}
fn twice() { // error[E0034]
print(2)
}
fn main() {
twice()
}
E0034: what this error means
Parameters
Each parameter has a name and a type. In the body, a parameter is a binding that holds the value of its argument (Values and memory).
fn describe(name: string, age: i64) -> string {
return "{name} is {age}"
}
fn main() {
print(describe("Eero", 7)) // Eero is 7
}
The rules:
- A parameter cannot change. An assignment to it is an error (E0001), and a parameter cannot be
mut(E0019). - To change the value, copy it into a
let mutbinding. Aletcan shadow a parameter (Shadowing). - Two parameters of one function cannot have the same name (E0039).
- A parameter of live code that the body never reads builds with a warning (W0014). A parameter named
_, or with a name that starts with_, gets no warning. - As a binding cannot, a parameter cannot take the name
Option,Result,Some,Ok,Error the name of an enum (Shadowing). - An argument must have the type of its parameter. Where the parameter is a
T?, aTbecomes aT?(Calls).
fn countdown(n: i64) {
let mut left = n
while left > 0 {
print(left)
left -= 1
}
}
fn main() {
countdown(3)
}
3 2 1
fn countdown(n: i64) {
while n > 0 {
print(n)
n -= 1 // error[E0001]
}
}
fn main() {
countdown(3)
}
E0001: what this error means
Unused parameters In development
A parameter can be named _. It binds nothing, and a parameter list can have _ more than once. Use it for a parameter that the function does not need.
fn first(x: i64, _: i64) -> i64 {
x
}
fn main() {
print(first(1, 2)) // 1
}
Return types
A function with a return type -> T gives a value of the type T. The value is the value of the last expression of its body, or the value of a return.
fn sign(n: i64) -> i64 {
if n < 0 {
return -1
}
if n == 0 { 0 } else { 1 }
}
fn main() {
print(sign(-5)) // -1
print(sign(8)) // 1
}
The rules:
- Each path through the body must end in a value or in a statement that never finishes (E0031, Reachability).
- The value must have the type
T(E0032). WhenTis an optionalU?, aUvalue becomes aU?(Types). - A function without a return type gives no value, so a call of it has no value (E0043).
- When the body of a function without a return type ends in a value, the program builds with a warning (W0004).
- For
return, see return.
fn half(n: i64) -> i64? {
if n % 2 == 1 {
return None
}
n / 2
}
fn main() {
print(half(8) ?? -1) // 4
print(half(7) ?? -1) // -1
}
A call without a value In development
A function without a return type has the return type unit. A call of it is a unit value, which a program can compare (unit).
fn log(text: string) {
print(text)
}
fn main() {
print(log("hi") == log("hi"))
}
Recursion
A function can call itself, and two functions can call each other.
fn factorial(n: i64) -> i64 {
if n <= 1 {
return 1
}
n * factorial(n - 1)
}
fn is_even(n: i64) -> bool {
if n == 0 { true } else { is_odd(n - 1) }
}
fn is_odd(n: i64) -> bool {
if n == 0 { false } else { is_even(n - 1) }
}
fn main() {
print(factorial(5)) // 120
print(is_even(10)) // true
}
A call can use space on the stack until it returns. When a recursion goes too deep, the stack is full and the program stops. This is not a panic, and output that the program did not write yet can be lost.
A function that calls itself on every path can never return. The program builds with a warning when each path reaches such a call before a way out (W0010).
For this check, a loop that can finish a pass is a way out. So a call after such a loop does not warn. To repeat without an end, write a loop.
fn count(n: i64) { // warning[W0010]
print(n)
count(n + 1)
}
fn main() {
count(0)
}
W0010: what this warning means
main
A program starts with a call of its function main. When that first call returns, the program ends with the exit code 0.
fn main() {
print("start") // start
}
The rules:
maintakes no parameters, or one[string]. It has no return type (E0029).- A
mainwith a[string]parameter gets the arguments of the program, without the name of the program (Input and output). - A program without
mainis an error (E0028). - A
returnin the first call ofmainends the program. - A program can call
mainagain. That call returns to its caller, as any call does. - A panic ends the program with the exit code 101 (panic).
fn main(args: [string]) {
print(args.len()) // 2
print(args[0]) // sauna
}
fn start() { // error[E0028]
print("start")
}
E0028: what this error means
A main that fails In development
main can return a unit | E. When it returns a failure, the program writes error: and the text of the error to standard error. Then it ends with the exit code 1 (A main that fails).
fn main() -> unit | string {
fail "no input"
}
panic
panic(message) stops the program. It writes the message to standard error and the program ends with the exit code 101.
fn check(n: i64) -> i64 {
if n < 0 {
panic("negative: {n}")
}
return n
}
fn main() {
print(check(2)) // 2
print(check(-1))
}
sisu: panic at example.sisu:3:9: negative: -1
The rules:
- The message is a
string(E0032). - Standard error gets
sisu: panic at, then the file, line and column ofpanic, then the message. - The program writes all its output from before the panic first.
- A call of
panichas the typenever(Types). So it can be a branch of anifor amatch, or the right side of??. - It can also be the value of a
break, or the end of a function body. - In other places, it is an error (E0043). Such places are an argument, an operand, a condition, an index, and the value of a
let, an assignment or areturn. - As the argument of
printoreprint, it is an error too (E0061).
fn main() {
let found: i64? = None
print(found ?? panic("no value"))
}
sisu: panic at example.sisu:3:20: no value
todo
todo marks code that you did not write yet. When it runs, it panics with the message not yet implemented. The program builds with a warning at each todo (W0003).
fn area(width: i64) -> i64 {
todo // warning[W0003]
}
fn main() {
print(area(3))
}
sisu: panic at example.sisu:2:5: not yet implemented
W0003: what this warning means
The rules:
todois a keyword. It has two forms:todo, andtodo("message"). The message is onestring.- With a message, the panic message is
not yet implemented:and then the message. todo()andtodo!()are errors (E0099).todohas the typenever, as a call ofpanichas. So it can be in the same places, such as the end of a function body.
fn main() {
todo("read the file") // warning[W0003]
}
sisu: panic at example.sisu:2:5: not yet implemented: read the file
W0003: what this warning means
Functions as values In development
A function is a value of a function type, such as (i64) -> i64. A program can pass it, store it and return it (Function types).
fn double(n: i64) -> i64 {
n * 2
}
fn apply(f: (i64) -> i64, x: i64) -> i64 {
f(x)
}
fn main() {
print(apply(double, 21)) // 42
}
Sisu