A function has a name, parameters with type annotations, an optional return type, and a body.
fn add(a: int, b: int) -> int {
a + b
}
fn greet(name: string) {
fmt.Println(f"Hello, {name}")
}Parameter types are required. The return type can be omitted; if so it defaults to ().
The last expression in the function body is the return value. Use return for early exits.
fn first_positive(nums: Slice<int>) -> Option<int> {
for n in nums {
if n > 0 {
return Some(n)
}
}
None
}Type parameters appear in angle brackets after the function name.
fn identity<T>(x: T) -> T {
x
}
fn swap<A, B>(pair: (A, B)) -> (B, A) {
(pair.1, pair.0)
}The compiler infers type arguments at call sites:
let x = identity(42) // T = int
let y = identity("hello") // T = stringExplicit type arguments are needed when inference has nothing to work with:
let empty = Slice.new<int>()
let counts = Map.new<string, int>()A type parameter can be constrained to types that implement an interface. See 11-interfaces.md
fn print_value<T: Display>(value: T) {
fmt.Println(value.to_string())
}Multiple bounds use +:
fn process<T: Display + Clone>(value: T) -> T {
fmt.Println(value.to_string())
value.clone()
}Multiple type parameters can each have their own bounds:
fn combine<T: Display, U: Debug>(a: T, b: U) -> string {
a.to_string() + b.debug_string()
}Two bounds are built in:
Comparablefor types that admit==and!=(everything except slices, maps, functions, and structs or tuples that contain them)Orderedfor types that admit<,>,<=, and>=(signed and unsigned integers, floats, andstring)
Ordered implies Comparable, so == and != are also available on a type bound by Ordered.
fn dedupe<T: Comparable>(xs: Slice<T>) -> Slice<T> { ... }
fn sorted<T: Ordered>(xs: Slice<T>) -> Slice<T> { ... }By default, parameters disallow rebinding inside the function body. Mark them mut to allow rebinding.
Additionally, if the function writes through the parameter in a way observable to the caller, marking the parameter mut requires the call-site binding to be mut as well.
fn sort_in_place(mut items: Slice<int>) {
// ...
}
let mut nums = [3, 1, 2] // arg mutable
sort_in_place(nums) // param mutable, ok
let nums = [3, 1, 2] // arg immutable
sort_in_place(nums) // param immutable, errorThis additional rule applies to Slice<T>, Map<K, V>, and any struct, tuple, or enum that recursively contains one. This rule does not apply to:
- Values that Go passes by copy, e.g.
int,string,bool,float64, plain structs, tuples. Ref<T>andChannel<T>. The purpose of pointers and channels is to share or transmit data, so mutation is implied.
Anonymous functions whose params appear between | pipes.
let double = |x: int| x * 2
let sum = |a: int, b: int| a + b
let produce_int = || 42Lambda parameter types can be omitted when inferable:
let doubled = [1, 2, 3].map(|x| x * 2)A block body allows multiple statements:
let process = |x: int| {
let y = x * 2
y + 1
}Lambdas capture variables from the enclosing scope:
let multiplier = 3
let scale = |x: int| x * multiplier← 04-control-flow.md |
06-structs-and-enums.md → |