Methods
An extend block adds functions to a type. A function in it whose first parameter is named self is a method, called on a value with .: counter.Advance(). A function without self is a static function of the type, called through its name with ::: Counter::Zero(5).
extension = "extend" type [ ":" interface ] "{" { function } "}"
receiver = "self" ":" ( type | "&" type | "&" "var" type )
struct Counter {
count: int;
step: int;
}
extend Counter {
func Zero(step: int) -> Counter {
return Counter { count: 0, step: step };
}
func Value(self: &Counter) -> int {
return self.count;
}
func Advance(self: &var Counter) {
self.count += self.step;
}
func Advanced(self: Counter) -> Counter {
var next = self;
next.Advance();
return next;
}
}
func Main() -> int {
var counter = Counter::Zero(5);
counter.Advance();
counter.Advance();
let ahead = counter.Advanced();
PrintLine("{} {}", counter.Value(), ahead.Value()); // 10 15
return 0;
}
A type may have any number of extend blocks, in any file of its package. The type's fields stay in the struct declaration; an extend block holds only functions. extend works for every named type — structs, enums, variants, unions — and for built-in types, as Extensions shows.
Receivers
The receiver is always named self, always comes first, and has its type written out like any other parameter. The type is the extended type, or a reference to it, and decides what the method may do:
| Receiver | The method gets | May change the caller's value | Callable on |
|---|---|---|---|
self: &Counter | the caller's value, to read | no | any Counter |
self: &var Counter | the caller's value, to change | yes | a var Counter |
self: Counter | a copy, read-only | no | any Counter |
A &T receiver is the usual choice for a method that only reads; &var T for one that changes the value in place; T for small values such as numbers and enum cases, or for a method that wants its own copy to work on, as Advanced does.
Inside a method, fields are always reached through self. There is no implicit scope: a bare count is an unknown name.
A receiver is read-only like every parameter, and a &T receiver cannot be written through:
error: cannot modify immutable receiver 'self'
help: take the receiver as 'self: &var Counter' to change the caller's value
error: cannot modify data through immutable reference '&Counter'
Calling methods
The value before the dot becomes self. It is borrowed when the receiver is a reference and copied when it is a value; the call site writes nothing extra in either case. The remaining arguments go in the parentheses.
A method with a &var T receiver needs a value it can write to:
let fixed = Counter::Zero(1);
fixed.Value(); // fine
fixed.Advance(); // error
error: cannot call 'Advance' on immutable 'fixed'
note: 'Advance' declares a writable receiver '&var Counter'
help: declare 'fixed' with 'var' to make it mutable
A method that reads may also be called on a value that has no name, such as a literal: Counter { count: 9, step: 1 }.Value().
| Kind | Declared | Called as |
|---|---|---|
| Method | func Value(self: &Counter) -> int | counter.Value() |
| Static function | func Zero(step: int) -> Counter | Counter::Zero(5) |
| Constructor | func Counter(step: int) -> Counter | Counter(5) |
A method is reached only through a value. It is not in scope as a free function, and it cannot be called through the type path:
error: name 'Value' is not defined in this scope
error: call to 'Counter::Value' expects 0 arguments, but 1 was provided
Each type has its own methods, so Rectangle and Circle may both have an Area without clashing; the type of the value before the dot decides which runs. Methods of one type overload like functions, by their parameters after self; see Overloading.
Rules and errors
| Mistake | Error |
|---|---|
The receiver given another name, this: &Counter | it is an ordinary parameter; counter.Value() then expects 1 argument, but 0 were provided |
self after another parameter | receiver 'self' must be the first parameter of method 'Value' |
self without a type, Value(self) | expected ':' and the receiver type after 'self' before ')' |
| A receiver of another type | receiver type '&Other' does not name the extended type 'Counter' |
A method named without parentheses, counter.Value | struct 'Counter' has no field 'Value' |
What else goes in extend
| Declaration | Page |
|---|---|
func T(...) -> T | Constructors |
func ~T(self: &var T) | Destructors |
func +(self: T, other: T) -> T and the other operators | Operators |
func [](self: &T, index: uint) -> E | Indexers |
extend T : Interface { … } | Interfaces |
A generic type's extend block names its type parameters, extend Pair<T> { … }, and a method may have type parameters of its own; see Generic methods. pub on each function makes it visible to other packages — pub on the struct does not reach its methods, and calling one that lacks it from another package is method 'Secret' is private to package 'Shapes'.
See also
- Structs — the data a method works on
- Extensions — methods on built-in types
- References — what
&Tand&var Treceivers borrow - Learn: Method, Mutating method
Overview
Declaring a struct, its fields and their visibility, building values with struct literals, reading and writing fields, value semantics and equality.
Constructors
Constructors in Rux: a receiverless function named after its type that returns the type, called as T(...), overloaded, and run by var x: T;.