Methods
An extend block adds functions to a type. A function in it whose first parameter is the receiver 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 = [ "&" [ "var" ] | "*" [ "var" ] ] "self"
struct Counter {
count: int;
step: int;
}
extend Counter {
func Zero(step: int) -> Self {
return Self { count: 0, step };
}
func Value(&self) -> int {
return self.count;
}
func Advance(&var self) {
self.count += self.step;
}
func Advanced(self) -> Self {
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 and always comes first. The extend block already gives its type, so the receiver is written only by how the method takes it:
| Receiver | The method gets | May change the caller's value | Callable on |
|---|---|---|---|
&self | the caller's value, to read | no | any Counter |
&var self | the caller's value, to change | yes | a var Counter |
self | the value itself, read-only | no | any Counter |
*self | a raw *Counter | no | an address |
*var self | a raw *var Counter | yes, inside unsafe | an address |
A &self receiver is the usual choice for a method that only reads; &var self for one that changes the value in place; self for small values such as numbers and enum cases, or for a method that wants its own copy to work on, as Advanced does. A self receiver of a move-only type takes the value over, so a named one is written (move value).Consume() — see Copy and move. The raw-pointer forms are for code that works with addresses; a field read through one needs unsafe, as on Pointers.
A receiver never names its type: self: &Counter is error: a receiver does not name its type, with help: write '&self'; the 'extend' block or interface gives the type, and self: &var Counter, self: Counter and self: *Counter are answered with &var self, self and *self the same way. Only a function inside extend or an interface takes a receiver: a free func Free(&self) is receiver '&self' is only allowed in an 'extend' block or an interface requirement.
Inside a method, fields are always reached through self. There is no implicit scope: a bare count is an unknown name.
Self names the extended type, with its type arguments, wherever a type or the type's name is written: in a signature (-> Self), a binding's type, a literal (Self { … }), a pattern, a constructor call (Self(…)), and a path such as Self::Zero(1). It is a plain alias, and outside an extend block or an interface it is 'Self' is only available inside an 'extend' block or an interface.
A receiver taken by value is read-only like every parameter, and a &self receiver cannot be written through:
error: cannot modify immutable receiver 'self'
help: take the receiver as '&var self' to change the caller's value
error: cannot modify data through immutable reference '&Counter'
Through &var self, a field is assigned with self.count = …, and the whole value is replaced with self = Self { … };.
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) -> 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 through a value. It is not in scope as a free function, Value() alone being name 'Value' is not defined in this scope. It may also be called through the type path with the receiver as its first argument, Counter::Value(counter), under the same borrowing, mutability and ownership rules as a call through the value.
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' |
A receiver that names its type, self: &Counter | a receiver does not name its type |
A receiver outside extend or an interface | receiver '&self' is only allowed in an 'extend' block or an interface requirement |
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(&var self) | Destructors |
func +(self, other: T) -> T and the other operators | Operators |
func [](&self, 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, throwing with -> Self throws E, and run by var x: T;.