Custom Operators in Swift
Classes and structures can provide their own existing operator implementation or create completely new ones as needed. In this article we will study how operators work and how to use them correctly.
Overloading operators
We will start by giving you an example that creates a custom operator to make a sum ( +
). The addition in arithmetic is a binary operator because it uses 2 elements and it is infix because the operator ( +
) is between these two numbers.
The example below defines a Vector3D
structure for a position in 3-dimensional space (x, y, z), followed by the definition of an operator function to allow the sum of 2 instances of Vector3D
.
The operator function is defined as a global function with a name that corresponds to the operator that you wish to overload ( +
). As the addition is a binary operator, this operator function takes 2 input parameters of type Vector3D
and returns a single value also of type Vector3D
.
In this implementation, the input parameters are named left
and right
to represent the Vector3D
that will be on the left side and the right side of the +
operator. The function returns a new Vector3D
whose values x
and y
and z
correspond to the sums of x
and y
and z
of the properties of the vectors given as parameters.
The function is defined globally, instead of being an instance method of a Vector3D
, making the operator usable as an infix operator between 2 existing Vector3D
.
Prefixed and Postfixed Operators
The previous example shows how to implement an infix binary operator. However classes and structures can also provide unary operator implementations. A unary operator operates on a single element and not two as a binary operator. They are prefixed if they precede the element (for example -a
) or postfixed if they follow the element (such as i++
).
To implement a prefixed or postfixed unary operator, just add the prefix
or postfix
keyword in front of the operator function:
This example implements the unary minus ( -x
) operator for the Vector3D
instances. This operator is prefixed and therefore uses the prefix
keyword.
For a single numeric value, the unary operator minus converted positive numbers to their negative equivalence and vice versa. The equivalent implementation for vectors is to perform this operation on x
,y
and z
.
Compound Assignment Operators
Compound assignment operators allow assignments ( =
) to be combined with other operations. For example, the addition (as operator +
) can be combined with assignment ( =
) to form an addition assignment ( +=
) which performs both in one operation. A compound assignment operator is declared with the use of the keyword inout
on the left operation to allow it to be modified in the body of the function. The example below implements a function to create an addition assignment for a Vector3D
:
It is important to note that the addition used in this operator is leveraging the implementation we saw earlier.
You can also combine declarations with either the prefix
or postfix
keyword such as below in this implementation of the prefixed prefix
operator ( ++x
) for a Vector3D
:
Equivalence Operator
Custom classes and structures do not have default implementations of equivalence operators, known as “equal to” (operator ==
) and "not equal to" (operator !=
). It is impossible in Swift to guess what is considered "equal" or not for your own custom types, because the meaning of "equal" depends on the roles these types play in your code.
To use the equivalence operator to check your types, it is necessary to provide an implementation of the operator in an infixed way:
In this example, we implement the operator “equal to” ( ==
) and conform our Vector3D to the Equatable
protocol. This protocol generates for us the !=
implementation and can make our life easier when trying to compare class storing Vector3D
such as arrays. In context, a Vector3D
is considered "equal to" another instance of Vector3D
if their properties x
, y
and z
are equal.
You can now use these operators to check if 2 instances of Vector3D
are equivalent.
Custom Operators
You can declare and implement your own custom operators in addition to the standard operators provided in Swift.
New operators can be used by using the operator
keyword and marked with the prefix, infix, or postfix keyword. Moreover, it can only be named using the character list provided here :
The example above defines a new prefix operator called +++
. It has no meaning in Swift and it will have its own meaning in the following example depending on the context: here with Vector3D
. To illustrate this example, +++
is treated as an operator to double the value. It doubles the value of the x
, y
, and z
of Vector3D
Priority and Associativity of Custom Infix Operators
Custom infix operators can also specify a priority and associativity. Check out the official documentation for an explanation of how associativity and priority affect operators.
The possible values for associativity
are left
, right
and none
. Associativity on the left associates the left element if it is written next to another associativity operator to the left of the same priority. In the same way, an operator with associativity on the right associates the right element if it is written next to another associativity operator on the right. An operator with no associativity can not be written next to other operators with the same priority.
The following example sets a new infix operator called +-+
with an associativity on the left and a priority above the Addition priority:
This operator adds the properties x
of the 2 vectors and at the same time subtracts the property y
from the second vector with the first one and add the 2 properties z
. Since it is an "additive" operator, it shoud have been given the same associativity and priority but the code shows a custom one as an example. For a complete list of the priorities and associativities of the operators provided by Swift in the standard library, take a look here .