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) };
}
}
| Parameter | Declared on | Fixed when | In count.With("apples") |
|---|---|---|---|
T | the type, Labeled<T> | the value is made | int32 |
U | the method, With<U> | each call | char8[..] |
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
- Generic types — declaring and extending generic types
- Methods — receivers and method calls
- Bounds — constraining type parameters
- Learn: Generic method
Generic Types
Structures and variants take type parameters. Every use names its type arguments, and each instantiation is a distinct type with its own layout.
Bounds
A bound, T: A + B, admits only type arguments that provide the interfaces' methods, and lets the generic body call them. It is checked at each use.