Function declarations
A function gives a block of statements a name, a list of typed parameters and, optionally, a result type. Calling it runs the block with the arguments bound to the parameters, and the call is worth the value the block returns.
function = [ attributes ] [ "pub" ] [ "unsafe" ] "func" name [ type-parameters ]
"(" [ parameter { "," parameter } [ "," ] ] ")"
[ "->" type ] [ "throws" type ] block
parameter = name ":" type [ "=" expression ]
| name ":" type "..."
func Add(left: int, right: int) -> int {
return left + right;
}
| Part | In Add | Meaning |
|---|---|---|
| Name | Add | An identifier; PascalCase by convention |
| Parameters | left: int, right: int | name: Type each, separated by commas; a trailing comma is allowed |
| Result type | -> int | The type of the value the function returns; omitted when there is none |
| Error type | none | throws E after the result, when the function can fail |
| Body | { return left + right; } | A block |
Parameters, default values and variadic parameters are described in Parameters. A name in angle brackets after the function name declares a type parameter, which makes the function generic.
Where functions are declared
| Place | Declares |
|---|---|
| The top level of a file | A free function of the package's root module |
A module A::B { } block | A free function of that module — see Modules |
An extend T { } block | A method, constructor, operator or destructor of T |
An extern { } block | A foreign function, without a body — see FFI |
asm func | A function whose body is assembly — see Assembly |
unsafe func declares a function whose caller must uphold something the compiler cannot check; a call to it needs an unsafe context — see Pointers. Every other function needs a body. A signature ending in ; is refused:
error: function 'Later' has no body
Declaration order does not matter: a function may call one declared further down the file, or in another file of the package. pub makes a function visible to other packages; see Visibility.
The result
| Written | The function |
|---|---|
no -> | Returns nothing. A bare return; leaves early; reaching the closing brace returns |
-> T | Returns a T. Every path through the body must end in return with a value |
-> () | Returns the unit value () |
-> T throws E, throws E | Can throw an E: it returns a T (or completes with ()) or throws |
Several values are returned as a tuple, an absent result as an optional.
func Greet(name: char8[..]) {
if name.length == 0 {
return;
}
PrintLine("Hello, {}!", name);
}
func Divide(dividend: int, divisor: int) -> (int, int) {
return (dividend / divisor, dividend % divisor);
}
The compiler checks that a function with a result type returns on every path, and that a function without one never returns a value:
error: function 'Sign' must return a value of type 'int' on every control-flow path
error: 'return' cannot have a value in a function with no return type
A function that declares throws E and no result type completes with () when it returns or reaches its end. Leaving out the result type of a function that does not throw is not the same as writing -> (). Only a unit-returning function produces a value of type () that can be stored or passed; a call to a function without a result is written as a statement on its own. See return for the statement itself.
Calls
A call is the function's name followed by one argument per parameter, in parentheses and in order. An argument may be labeled with its parameter's name, as in Clamp(150, high: 255) — see Named arguments. Arguments are evaluated from left to right, then bound to the parameters, and the body runs:
func Note(label: char8[..], value: int) -> int {
PrintLine("evaluating {}", label);
return value;
}
func Main() -> int {
// Prints "evaluating first", then "evaluating second", then 12.
PrintLine("{}", Pair(Note("first", 1), Note("second", 2)));
return 0;
}
func Pair(first: int, second: int) -> int {
return first * 10 + second;
}
A call is an expression of the result type and may appear wherever a value of that type fits — as an argument to another call, in an operator, on the right of a binding.
Each argument must convert to its parameter's type as an initializer would: an integer widens to a wider integer of the same signedness, and an unsuffixed integer literal takes the integer type of its parameter, so Half(9) works for a uint8 parameter. A function may call itself, directly or through others; each call has its own parameters and locals.
A call to a function that declares throws is a throw site and is marked with try, as in let n = try Parse(text);; an unmarked one is call to 'Parse' can throw 'ParseError' but is not marked with 'try'. See Errors.
Rules and errors
| Mistake | Error |
|---|---|
| An argument of another type | argument 1 to 'Square' has type 'float64', but parameter 'value' requires 'int' |
| Too many or too few arguments | call to 'Square' expects 1 argument, but 2 were provided |
| A name in the wrong case | name 'square' is not defined in this scope, with did you mean 'Square'? |
A missing return on some path | function 'Sign' must return a value of type 'int' on every control-flow path |
| A value returned from a function without a result | 'return' cannot have a value in a function with no return type |
A body-less signature outside extern | function 'Later' has no body |
Names are case-sensitive. Several functions may share a name when their parameters differ; see Overloading.
See also
- Parameters — by-value parameters, defaults and variadics
- Function types — functions as values
- Main — the entry point of a program
- Generics — functions over type parameters
- Learn: Function, Return, Recursion
Patterns
Every pattern form — literals, ranges, bindings, _, tuples, structures, enum and variant cases, typed patterns, none and .Some — alternatives and guards.
Parameters
How arguments reach a function: read-only by-value parameters, references, default values evaluated at each call, named arguments, and variadic parameters with spread.