Extensions
extend is not limited to the types a package declares. It can add methods to a built-in type, and from then on every value of that exact type has them: answer.IsEven(), scores[..].Sum().
extend int32 {
func IsEven(self: int32) -> bool {
return self % 2 == 0;
}
}
extend int[..] {
func Sum(self: int[..]) -> int {
var total = 0;
for value in self {
total += value;
}
return total;
}
}
extend var int[..] {
func Fill(self: var int[..], value: int) {
for i in 0..self.length {
self[i] = value;
}
}
}
extend char8[..] {
func Initial(self: char8[..]) -> char8 {
return self[0];
}
}
func Main() -> int {
let n: int32 = 6;
PrintLine("{} {}", n.IsEven(), 7i32.IsEven()); // true false
let scores = [3, 4, 5];
PrintLine("{} {}", scores[..].Sum(), scores[1..].Sum()); // 12 9
var buffer: int[3] = [0, 0, 0];
buffer[..].Fill(2);
PrintLine("{}", "rux".Initial()); // r
return 0;
}
The receiver follows the rules of Methods. Numbers and slices are usually taken by value — self: int32, self: int[..] — because copying them is as cheap as borrowing them. A slice is a view, so a by-value slice receiver still sees the caller's elements, and a var T[..] receiver can write them.
What can be extended
| Type | Extendable | Example |
|---|---|---|
A primitive: integer, float, bool, character | yes | extend int32 { … } |
| A slice of one element type | yes | extend int[..] { … } |
| A writable slice | yes | extend var int[..] { … } |
| A struct, enum, variant or union | yes | extend Point { … } |
| A type alias | the aliased type is extended | extend Celsius { … } adds to float64 |
An optional, fallible, sum or () | no | cannot extend native type 'int32?' |
| A tuple | no | extend (int, int) is refused |
| A slice of a type parameter | no | extend T[..] is refused |
An alias names its type and hides nothing, so extend Celsius with type Celsius = float64; gives every float64 in the package those methods. Methods meant for one kind of value belong on a type of its own, such as a struct with a single field.
A native optional, fallible, sum or unit has no declaring package to own its methods:
error: cannot extend native type 'int32?'
note: a sum, optional, fallible, or unit type has no declaring package to own methods or interface implementations
help: write a generic function that takes the native type as a parameter
extend T[..] for every element type is not available; extend each slice type separately, or write a generic function that takes a T[..]:
error: cannot extend slice type 'T[..]' because element type 'T' is not defined
help: extend a slice of one concrete element type, for example 'extend int[..]'
rux 0.4.0 passes
extend int[3] { … } through rux check, but the build then fails. Extend the slice type instead, and call the method on a view of the array, numbers[..].One exact type
Methods belong to exactly the type that was extended. extend int32 does not reach int or int64, extend int[..] does not reach uint8[..], and an array is not a slice:
error: type 'int32' has no field 'Double'
error: type 'int[3]' has no field 'Sum'
An array reaches slice methods through a view of itself: scores[..].Sum(), or scores[2..].Sum() for part of it.
In rux 0.4.0 writability is part of the type for method lookup too. A view of a var array is a var int[..], which finds the methods of extend var int[..] but not those of extend int[..]:
error: slice type 'var int[..]' has no member 'Sum'
note: available slice members are 'data' and 'length'
Bind such a view to a read-only slice first, let all: int[..] = buffer;, and call the read-only methods on that.
Interfaces
An extension written extend T : Interface { … } makes the type implement an interface, and works for built-in types as for declared ones; see Interfaces.
See also
- Methods — receivers and calls
- Slices — read-only and writable views
- Type aliases — what an alias does and does not create
- Learn: Extension
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;.
Overview
Scalar enumerations: declaring enum cases, the backing integer type and explicit values, naming cases, as conversions in both directions, and matching.