Generic Methods

A method can be generic in two ways. A method of a generic type uses the type's parameters, fixed when the value was made. And any method — on a generic type or not, with a receiver or without — may declare type parameters of its own, chosen afresh at each call.

Methods of a generic type

Inside extend Box<T>, T means whatever the receiver's instantiation says. Receivers, parameters and results are written with it:

struct Labeled<T> {
    label: char8[..];
    value: T;
}

extend Labeled<T> {
    func Labeled(label: char8[..], value: T) -> Labeled<T> {
        return Labeled<T> { label: label, value: value };
    }

    func Get(self: &Labeled<T>) -> T {
        return self.value;
    }

    func Set(self: &var Labeled<T>, value: T) {
        self.value = value;
    }
}
var count = Labeled<int32>("count", 7);
count.Set(15);
let n = count.Get();    // an int32

The type arguments are written on the type name for a constructor or an associated call, Labeled<int32>("count", 7), and come from the receiver for an instance call. Each method is compiled for each instantiation that calls it.

Methods with their own type parameters

A method declares its own type parameters after its name, exactly as a generic function does. They are independent of the type's:

extend Labeled<T> {
    func With<U>(self: &Labeled<T>, other: U) -> (T, U) {
        return (self.value, other);
    }

    func Map<U>(self: &Labeled<T>, change: func(T) -> U) -> Labeled<U> {
        return Labeled<U> { label: self.label, value: change(self.value) };
    }
}
ParameterDeclared onFixed whenIn count.With("apples")
Tthe type, Labeled<T>the value is madeint32
Uthe method, With<U>each callchar8[..]

A method's own parameters are inferred from its arguments, or written after the method name:

let fruit = count.With("apples");     // U = char8[..], inferred
let flag = count.With<bool>(true);    // U = bool, written
let half = count.Map(Half);           // U from Half's result type

A method's result may be a different instantiation of its own type, as Map returns Labeled<U>.

Associated calls

A method without a receiver is called through its type. On a non-generic type, the method's own type arguments follow the method name:

struct Factory {
    bias: int;
}

extend Factory {
    func Width<T>() -> uint {
        return sizeof(T);
    }

    func Echo<T>(value: T) -> T {
        return value;
    }

    func Read<T>(self: &Factory, value: T) -> T {
        return value;
    }
}
let w = Factory::Width<int32>();      // 4: nothing to infer from, so written
let e = Factory::Echo(42i32);         // T inferred from the argument
let factory = Factory { bias: 5 };
let r = factory.Read<bool>(true);     // an instance call, written

On a generic type, an associated call has two sets of parameters to fill: the receiver type's and the method's. They are written in one list after the method name — the receiver type's arguments first, then the method's:

struct Holder<T> {
    value: T;
}

extend Holder<T> {
    func OtherWidth<U>() -> uint {
        return sizeof(T) + sizeof(U);
    }

    func Pair<U>(self: &Holder<T>, other: U) -> (T, U) {
        return (self.value, other);
    }
}
let total = Holder::OtherWidth<int32, int64>();    // T = int32, U = int64: 12

A list that does not fill both fails with cannot resolve type arguments for method 'OtherWidth'. An instance call needs no such list for the type's parameters: holder.Pair<int64>(34) takes T from holder and int64 for the method's own parameter.

Bounds on method parameters

A method's own type parameters may carry bounds, checked at each call like a function's:

interface Scored {
    func Score() -> int;
}

extend Factory {
    func Score<T: Scored>(self: &Factory, value: &T) -> int {
        return self.bias + value.Score();
    }
}
error: type argument 'int' does not satisfy interface bound 'Scored' on type parameter 'T'
  note: interface 'Scored' requires method 'Score', which type 'int' does not implement
  note: type parameter 'T' of method 'Score' is bound by 'Scored'
  help: implement the interface, as in 'extend int: Scored { ... }'

What cannot be generic

The special methods have fixed shapes with no type parameters of their own: destructors, the copy and move operations = and <-, and the indexers [] and []=. They may still use the type parameters of a generic type they belong to.

See also