Intro
This installement of my Rust learning series deals with Chapter 12. Operator Overloading
I'm actually a bit wary of operator overloading, as it's easily ab/overused, and objects then behaving in a opaque "magical" way. Sometimes it's just clearer to spell a method out instead of binding it to an operator that only superficially relates to the action being carried out. So I will keep this post short.
There's a number of traits in the std::ops
module that help with operator
overloading – from std::ops::Add, std::ops::BitAnd, to std::ops::Shr for
right shifting, etc.
Arithmetic and Bitwise Operators
Example of implementing +
for a complex
type Complex<T>
:
use std::ops::Add;
impl<T> Add for Complex<T>
where
: Add<Output = T>,
T{
type Output = Self;
fn add(self, rhs: Self) -> Self {
{
Complex : self.re + rhs.re,
re: self.im + rhs.im,
im}
}
}
The Complex<T>
type has a type
parameter (presumably numeric); we implement Add for this generic type.
We also say that we implement Add for T's that can be added to
themselves.
Unary operators
Rusts signed ints support the negative sign -
, and bools as well as ints support negation
!
– for ints this results in bitwise
complement.
Binary operators
Binary arithmetic operators are supported by all numeric types. Ints
and bools implement bitwise operators. The +
operator can be used on Strings and &str
slices, however &str mustn't be left-hand side (performs poorly).
Should use write!()
for building up
strings in general.
Compound Assignment Operators
These are known as "Augmented assignment operators" in Python, i.e.
things like x += 1
. Rust defines a separate set of traits
for them, from AddAssign to ShrAssign.
Equivalence Comparisons
The relevant trait here is std::cmp::PartialEq
. It defines two methods
.eq()
and .ne()
; the latter has a default implementation
(negating eq()
), so typically only need to
implement one method – or derive it with #[derive(PartialEq)]
Side note, the trait is called PartialEq as the ==
operator does not implement full equivalence relations, as that doesn't
work with floats as defined by IEEE (specifically, 0.0/0.0 needs to be
NaN according to the standard, and NaN being unequal to everything
else). If you don't care for floats there's an Eq trait as well (which
too can be #[derive(Eq)]
).
Ordered Comparisons
The only method that needs to be implemented for the PartialOrd trait
is partial_cmp()
, which needs to return
one of the enum Ordering
values Less,
Equal, or Greater wrapped in an Option. In analog to equivalence above
there's also an Ord trait which defines a method cmp()
, which should return an Ordering.
Index and IndexMut
These implement indexing operations a[i]
. Arrays support indexing directly, but for
other types indexing usually is a shorthand for a.index(i)
, where .index()
is from the Index trait. IndexMut comes
into play where the expression is being borrowed mut or assigned. The
index i does not have to be int btw., e.g. the HashMap
collection implements Index<&str>
to allow indexing by
string.
Other Operators
Operators that can't be overloaded: "?" for error-checking, &&
and ||
which operate only on bools. The ..
and
..=
operators, the &
and
=
operators. The func call f()
can't be overloaded either (use a closure
instead).
Overloading of *
the .
operator are handled in a later chapter.
Coda
Short post, as I'm not much of a fan of operator overloading outside of some specific use cases (maybe around custom quasi-numeric types or collections).