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.

ParameterThe function receivesCan change the caller's valueThe caller passes
x: Tits own copy, read-onlynoany T; a move-only value as <-value
x: &Tthe caller's value, borrowed for readingnoa named T
x: &var Tthe caller's value, borrowed for writingyesa var T
x: T[..]a read-only view of the caller's elementsnoa slice, or an array viewed as one
x: var T[..]a writable view of the caller's elementsthe elements, yesa 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;
}
CallvaluelowhighResult
Clamp(150)1500100100
Clamp(42, 50)425010050
Clamp(150, 0, 255)1500255150

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 passes low too. 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, self included, 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;
}
Callvalues inside SumResult
Sum()empty0
Sum(1, 2, 3)1, 2, 36
Largest(4)rest is empty4

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);
}
Only the last parameter may be variadic.
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