lang::keyword_arguments¶
Keyword arguments and parameter defaults: a call may name any parameter (volume(depth: 4, width: 2)), and a parameter may declare a default known while compiling (fn volume(width: i64, height: i64 = 2), mode: Mode = Mode::Fast, parent: Option<Window> = None) that is spliced into every call omitting it, meaning what it means where the function is declared. Positional arguments come first, then names. Both are caller-side spellings rewritten into the callee's declared order before type checking, so the calling convention is unchanged. A name on a method call is matched when every type declaring that method name would rewrite the call identically; when they disagree the call is reported (GR0013) rather than guessed.
A call may name the parameter each argument fills, and a parameter may declare a default that callers can leave out.
fn volume(width: i64, height: i64 = 2, depth: i64 = 3) -> i64 {
width * height * depth
}
fn main() {
println("{}", volume(2)) // 12
println("{}", volume(2, 3)) // 18
println("{}", volume(width: 2, height: 3, depth: 4)) // 24
println("{}", volume(depth: 4, width: 2, height: 3)) // 24
println("{}", volume(2, depth: 10)) // 40
}
Both are spellings at the call site. The compiler rewrites every call into the order its callee declares before type checking, so the compiled program is the same one you would get by writing the arguments in order. The calling convention is untouched, and the bytecode VM, the JIT, and native builds all compile the identical call.
Naming an argument¶
Write name: value in place of a positional argument. A name selects the
parameter it fills, so named arguments may appear in any order.
Positional arguments come first, then names:
Once a name is used the remaining positions are no longer in written order, so every later argument needs a name too.
A name has to name a parameter of the callee, and may be given once:
volume(depht: 3) // error[GR0013]: `depht` is not a parameter
volume(width: 1, width: 2) // error[GR0013]: `width` is given twice
Declaring a default¶
Write = value after a parameter's type. A call that omits the parameter
gets that value spliced in at its position.
A default must be a value known while compiling: an integer, float,
string, char, byte, or bool literal, optionally negated (-1), a const
or an associated const, integer arithmetic over those, or a comptime
expression. A scalar const or arithmetic over consts folds to a literal
that is spliced into every call that omits it. A const is resolved where
the function is declared, so every call receives the declaration's value:
const DEFAULT_TIMEOUT: i64 = 30
fn connect(host: String, timeout: i64 = DEFAULT_TIMEOUT * 1000) -> String {
f"{host} {timeout}"
}
fn main() {
println(connect("db"))
}
A default may also hold data: an enum variant (Mode::Fast, None,
Some(4), Ok(0)), a struct construction, a tuple, an array, a Vec,
Map, or Set literal, a function, or a const of any type, built from
values that are themselves known while compiling. Every name in it means
what it means where the function is declared, so a call site that has
another Mode or ORIGIN in scope still receives the declaration's:
enum Profile { Core, Compat }
struct Size { w: i64, h: i64 }
fn open(
title: String
profile: Profile = Profile::Core
size: Size = Size { w: 800, h: 600 }
parent: Option<String> = None
tags: Vec<String> = #[]
) -> String {
f"{title} {size.w}x{size.h} {parent} {tags.len()}"
}
fn main() {
println(open("main"))
println(open("tools", size: Size { w: 320, h: 200 }, parent: Some("main")))
}
A default whose value is computed at run time - a call to a function, a
closure, a static, or another parameter - is rejected:
fn f(a: i64, b: i64 = a + 1) // error[GR0014]: a parameter default must be a value known while compiling
Defaults are per call site. Two calls to the same function never share a value, so a default of a String or any other owned type is safe.
Methods and associated functions¶
Both forms work on methods and associated functions:
struct Rect { w: i64, h: i64 }
impl Rect {
fn make(w: i64, h: i64 = 5) -> Rect { Rect { w: w, h: h } }
fn scaled(&self, factor: i64 = 2) -> i64 { self.w * self.h * factor }
}
let r = Rect::make(w: 3) // h defaults to 5
r.scaled() // factor defaults to 2
r.scaled(factor: 10)
A method call binds its names and defaults against the declaration its receiver's type reaches, so another type that happens to declare a method of the same name, with other parameters, changes nothing:
struct Socket { host: String }
struct Database { url: String }
impl Socket { fn connect(&self, timeout: i64 = 30) -> String { f"{self.host} {timeout}" } }
impl Database { fn connect(&self, retries: i64 = 3) -> String { f"{self.url} {retries}" } }
fn main() {
let s = Socket { host: "a" }
println(s.connect())
println(s.connect(timeout: 5))
}
A receiver whose type is a generic parameter uses the declaration of the
trait that bounds it. Only a receiver whose type is not settled where the
call is written, with declarations that disagree, is reported (GR0013);
passing every argument by position always works.
Diagnostics¶
| Code | Meaning |
|---|---|
GR0013 |
A name that matches no parameter, is given twice, follows a positional argument, or is on a method whose receiver's type is not settled where several types declare it differently. |
GR0014 |
A parameter default that is not a value known while compiling. |
gos explain GR0013 and gos explain GR0014 expand both.