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

TypeExtendableExample
A primitive: integer, float, bool, characteryesextend int32 { … }
A slice of one element typeyesextend int[..] { … }
A writable sliceyesextend var int[..] { … }
A struct, enum, variant or unionyesextend Point { … }
A type aliasthe aliased type is extendedextend Celsius { … } adds to float64
An optional, fallible, sum or ()nocannot extend native type 'int32?'
A tuplenoextend (int, int) is refused
A slice of a type parameternoextend 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[..]'
Fixed-size arrays.
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