Parameters
A parameter is written name: Type. Each call binds the parameter to its argument for that one run of the body. A parameter is read-only: the body may read it but never assign to it, and what the caller can see changed depends only on the parameter's type.
parameter = name ":" type [ "=" expression ] // an ordinary parameter, optionally with a default
| name ":" type "..." // a variadic parameter
Passing modes
The parameter's type decides how the argument is passed. The call site writes nothing extra for any of them, except the arrow that moves a value which cannot be copied.
| Parameter | The function receives | Can change the caller's value | The caller passes |
|---|---|---|---|
x: T | its own copy, read-only | no | any T; a move-only value as <-value |
x: &T | the caller's value, borrowed for reading | no | a named T |
x: &var T | the caller's value, borrowed for writing | yes | a var T |
x: T[..] | a read-only view of the caller's elements | no | a slice, or an array viewed as one |
x: var T[..] | a writable view of the caller's elements | the elements, yes | a writable slice, such as buffer[..] of a var array |
References are described in References, views in Slices, and moves in Copy and move.
Parameters are read-only
Assigning to a parameter is an error, whatever its type:
func Bump(n: int) -> int {
n = n + 1; // error
return n;
}
error: cannot modify parameter 'n'
note: a parameter is immutable
help: take 'n' as '&var int' to change the caller's value, or move it into a 'var' local
A function that needs a value it can change declares a local of its own and starts it from the parameter. A function that must change the caller's value takes &var T:
func Countdown(from: int) {
var remaining = from;
while remaining > 0 {
Print("{} ", remaining);
remaining -= 1;
}
PrintLine("liftoff");
}
func Tick(count: &var int) {
count += 1;
}
var is never written before a parameter name. func Count(var n: int) is refused with mutable parameter syntax 'var n: T' has been removed, and the help names the two forms above.
Default values
A parameter may give a default value after =. A call may then stop before it, and the default fills the gap:
func Clamp(value: int, low: int = 0, high: int = 100) -> int {
if value < low {
return low;
}
if value > high {
return high;
}
return value;
}
| Call | value | low | high | Result |
|---|---|---|---|---|
Clamp(150) | 150 | 0 | 100 | 100 |
Clamp(42, 50) | 42 | 50 | 100 | 50 |
Clamp(150, 0, 255) | 150 | 0 | 255 | 150 |
The rules:
- Defaults come last. A parameter without a default may not follow one with a default, because arguments always fill parameters from the left:
parameter 'second' without a default value cannot follow a parameter with a default value. - There are no named arguments. A call cannot skip a parameter in the middle; to change
high, it passeslowtoo.Clamp(150, high: 255)is a syntax error. - The default has the parameter's type:
default value type 'float64' does not match parameter type 'int'. - A default is evaluated at each call that omits it, in the caller's scope, as if the caller had written it. A default with an effect has it once per such call:
func Tick() -> int {
PrintLine("tick");
return 7;
}
func Show(value: int = Tick()) {
PrintLine("value {}", value);
}
func Main() -> int {
Show(); // tick, value 7
Show(1); // value 1 — Tick is not called
Show(); // tick, value 7
return 0;
}
- A default cannot read another parameter,
selfincluded, since it is evaluated where no parameter exists yet. Write an overload that passes the value instead:
// func Repeat(text: char8[..], count: uint = text.length) is refused
func Repeat(text: char8[..], count: uint) -> uint {
return text.length * count;
}
func Repeat(text: char8[..]) -> uint {
return Repeat(text, text.length);
}
error: a default value cannot refer to parameter 'text'
help: add an overload without this parameter that passes the value it should default to
#source as a default.A
#source value written as a default, such as line: uint = #source.line, is expanded where the function is declared in rux 0.4.0, not at the call, so every call sees the declaration's line.Variadic parameters
A last parameter written name: T... accepts any number of arguments of type T, zero included. Inside the function it is a read-only slice, T[..]:
func Sum(values: int...) -> int {
var total = 0;
for value in values {
total += value;
}
return total;
}
func Largest(first: int, rest: int...) -> int {
var largest = first;
for value in rest {
if value > largest {
largest = value;
}
}
return largest;
}
| Call | values inside Sum | Result |
|---|---|---|
Sum() | empty | 0 |
Sum(1, 2, 3) | 1, 2, 3 | 6 |
Largest(4) | rest is empty | 4 |
Ordinary parameters may come before the variadic one, and each still needs its argument: Largest() is call to 'Largest' expects at least 1 argument, but 0 were provided. The elements are read-only — values[0] = 1 is cannot modify elements through read-only slice 'int[..]'.
The element type may be an interface, and then each argument may be a different type that implements it. That is how PrintLine is declared: args: Display... takes an int, a float64 and a string in one call.
Spread
expression... as the argument for a variadic parameter passes the elements of a slice or array as the arguments:
let more = [4, 5, 6];
Sum(more...); // 15
Sum(more[1..]...); // 11
A spread must be the only argument for the variadic parameter. Mixing it with separate values is an error:
error: spread argument to 'Sum' must be the only argument for variadic parameter 'values'
A spread is how one variadic function hands everything it received to another:
func Average(values: int...) -> int {
if values.length == 0 {
return 0;
}
return Sum(values...) / (values.length as int);
}
rux 0.4.0 does not check this yet:
func Bad(args: int32..., last: int32) is accepted, and the build then fails or crashes. Keep the variadic parameter last.A variadic parameter may also follow a format string marked #Format(), which lets the compiler count placeholders against arguments.
See also
- Function declarations — the shape of a function
- Overloading — how a call chooses among functions with defaults and variadics
- References —
&Tand&var Tparameters - Copy and move — passing values that cannot be copied
- Learn: Default argument, Variadic, Mutable reference