Operator overload
The operators from the Operators part are not reserved for numbers. A type of your own can say what +, == or < mean for it, by declaring a function whose name is the operator. The point is not cleverness: money adds to money, and rent + food says that plainly where Money { cents: rent.cents + food.cents } buries it.
An operator is a function
Operator functions go in an ordinary extend block, next to the constructor:
// `self` is the left operand and the second parameter is the right one. Taking the right
// operand by value lets it be a value built on the spot, as in `rent == Money(120000)`.
// `other: &Money` works as well, but a borrow needs a named value on the right.
func +(self: &Money, other: Money) -> Money {
return Money { cents: self.cents + other.cents };
}
The function's name is the operator itself. When the compiler meets rent + food with a Money on the left, it calls this function with self borrowing rent and other holding food:
| You write | The compiler calls | self | Right operand | Result |
|---|---|---|---|---|
rent + food | + | rent | food | Money |
rent - food | - | rent | food | Money |
rent * 3 | * | rent | 3 | Money |
rent == Money(120000) | == | rent | a new Money | bool |
food < rent | < | food | rent | bool |
Once + is declared, the compound form works too: sum += food adds to a var sum of type Money.
The two sides need not match
// The two sides need not have the same type. Money times a count makes sense; money times
// money does not, so only this one exists.
func *(self: &Money, count: int64) -> Money {
return Money { cents: self.cents * count };
}
The second parameter's type is whatever the right operand should be. Money times a count makes sense; money times money does not, so rent * rent is simply not defined. The order matters as well: self is always the left operand, so this function makes rent * 3 work but not 3 * rent.
Comparisons answer with bool
// Comparisons answer with `bool`, not with Money.
func ==(self: &Money, other: Money) -> bool {
return self.cents == other.cents;
}
func <(self: &Money, other: Money) -> bool {
return self.cents < other.cents;
}
A declared == replaces the field-by-field comparison from Structural equality. For Money both give the same answer, but a type like the Fraction from Equatable could now make 1/2 == 2/4. And from these two, the compiler can work out !=, >, <= and >= — the subject of the next lesson.
Only what you declare
Money has no /, so rent / 2 is an error rather than something invented on Money's behalf. Each operator you leave out is a decision: there is no sensible meaning for money divided by money, and dividing by a count would raise the question of what to do with the leftover cent.
The program
The whole lesson is one package in the Examples repository. Its comments explain every step.
// The operators from the Operators part are not reserved for numbers. A type of your own can say
// what `+`, `==` or `<` mean for it, by declaring a function whose name is the operator.
//
// The point is not cleverness. Money adds to money, and `rent + food` says that plainly where
// `Money { cents: rent.cents + food.cents }` buries it. A declared `==` also replaces the
// field-by-field comparison from the previous lesson, so a type decides what "equal" means.
import Io::PrintLine;
// Money as a whole number of cents, so no fraction of a cent ever goes missing.
struct Money {
cents: int64;
}
extend Money {
func Money(cents: int64) -> Money {
return Money { cents: cents };
}
// `self` is the left operand and the second parameter is the right one. Taking the right
// operand by value lets it be a value built on the spot, as in `rent == Money(120000)`.
// `other: &Money` works as well, but a borrow needs a named value on the right.
func +(self: &Money, other: Money) -> Money {
return Money { cents: self.cents + other.cents };
}
func -(self: &Money, other: Money) -> Money {
return Money { cents: self.cents - other.cents };
}
// The two sides need not have the same type. Money times a count makes sense; money times
// money does not, so only this one exists.
func *(self: &Money, count: int64) -> Money {
return Money { cents: self.cents * count };
}
// Comparisons answer with `bool`, not with Money.
func ==(self: &Money, other: Money) -> bool {
return self.cents == other.cents;
}
func <(self: &Money, other: Money) -> bool {
return self.cents < other.cents;
}
}
func Main() -> int {
let rent = Money(120000);
let food = Money(35050);
// Each operator below calls one of the functions above.
let total = rent + food;
let left = rent - food;
let quarter = rent * 3;
PrintLine("rent + food = {} cents", total.cents);
PrintLine("rent - food = {} cents", left.cents);
PrintLine("rent * 3 = {} cents", quarter.cents);
PrintLine("rent == 120000 cents: {}", rent == Money(120000));
PrintLine("food < rent: {}", food < rent);
// Only the operators declared exist. Money has no `/`, so `rent / 2` is an error rather
// than something invented on Money's behalf.
return 0;
}
Run it
cd Examples/Interfaces/OperatorOverload
rux run
rent + food = 155050 cents
rent - food = 84950 cents
rent * 3 = 360000 cents
rent == 120000 cents: true
food < rent: true
Common mistakes
rent / 2 fails with error: operator '/' cannot combine left operand 'Money' with right operand 'int'. Declare / if Money should have it.* was declared with Money on the left, so 3 * rent fails with error: operator '*' cannot combine left operand 'int' with right operand 'Money'. Write rent * 3.Declare
== as func ==(self: &Money, other: &Money) -> bool and rent == Money(120000) fails with error: cannot pass 'Money' to parameter of type '&Money': a borrow needs a named value, not one built on the spot. Take the right operand by value, as the lesson does.Try it yourself
- Declare
/taking anint64count, and printrent / 3. What happens to the leftover cent? - Use
+=in a loop to add up an array ofMoneyvalues. - Write a
Vector { x: float64; y: float64; }with+for two vectors and*for a vector and afloat64.
Learn more
- Derived operator —
!=,>,<=and>=for free - Arithmetic — the operators on numbers
- Constructor — the
Money(120000)used on the right of== - Arithmetic operators in the Rux Reference