v0.4.0

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:

ReceiverThe method getsMay change the caller's valueCallable on
&selfthe caller's value, to readnoany Counter
&var selfthe caller's value, to changeyesa var Counter
selfthe value itself, read-onlynoany Counter
*selfa raw *Counternoan address
*var selfa raw *var Counteryes, inside unsafean 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().

KindDeclaredCalled as
Methodfunc Value(&self) -> intcounter.Value()
Static functionfunc Zero(step: int) -> CounterCounter::Zero(5)
Constructorfunc Counter(step: int) -> CounterCounter(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

MistakeError
The receiver given another name, this: &Counterit is an ordinary parameter; counter.Value() then expects 1 argument, but 0 were provided
self after another parameterreceiver 'self' must be the first parameter of method 'Value'
A receiver that names its type, self: &Countera receiver does not name its type
A receiver outside extend or an interfacereceiver '&self' is only allowed in an 'extend' block or an interface requirement
A method named without parentheses, counter.Valuestruct 'Counter' has no field 'Value'

What else goes in extend

DeclarationPage
func T(...) -> TConstructors
func ~T(&var self)Destructors
func +(self, other: T) -> T and the other operatorsOperators
func [](&self, index: uint) -> EIndexers
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