# Swift Custom Operators: Define and Use Your Own Symbols

## Pro Summary

Operator declarations (`prefix`, `infix` or `postfix operator`) are global, take no access modifier, and are visible to every module that imports the declaring module; only the implementing functions take `public`. An infix operator declared without a group gets `DefaultPrecedence`, which is non-associative and has no ordering relative to the arithmetic and comparison groups, so mixed expressions need parentheses until you assign a `precedencegroup`. Fixity comes from whitespace, not just the declaration: in `a +~b`, `+~` is parsed as a prefix operator.

```
precedencegroup ExponentiationPrecedence {
    associativity: right
    higherThan: MultiplicationPrecedence
}

infix operator **: ExponentiationPrecedence

extension Int {
    public static func ** (base: Int, exponent: Int) -> Int {
        precondition(exponent >= 0, "negative exponent")
        var result = 1
        for _ in 0..<exponent { result *= base }
        return result
    }
}
// 2 ** 3 ** 2 == 512, 2 * 3 ** 2 == 18
```

Requires: `precedencegroup` syntax since Swift 3 (SE-0077); the Swift 2 form `infix operator ** { associativity right precedence 160 }` is now a compile error.

Gotchas:

- Without a precedence group, `2 <~> 3 + 4` fails ("adjacent operators are in unordered precedence groups 'DefaultPrecedence' and 'AdditionPrecedence'") and `a <~> b <~> c` fails as non-associative, so the group is optional only until you mix operators.
- Whitespace determines fixity: `1 +~ 2` and `1+~2` are infix, but `1 +~2` parses `+~` as a prefix operator and fails to compile.
- A `.` may appear in an operator only if the operator starts with `.` (`.+.` is valid, `+.+` is not), postfix operators may not start with `?` or `!`, and tokens such as prefix `&` are reserved.
- A local `infix operator` declaration silently shadows an imported one with the same name: re-declaring another module's right-associative `^^` in `AdditionPrecedence` turns `a ^^ b ^^ c` from `a ^^ (b ^^ c)` into `(a ^^ b) ^^ c`.
- `associativity` accepts only lowercase `left`, `right` or `none`, and statements in a `precedencegroup` body can't be separated with `;`.

Primary sources:

- [The Swift Programming Language: Custom Operators](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/advancedoperators/#Custom-Operators)
- [The Swift Programming Language: Lexical Structure, Operators](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/lexicalstructure/#Operators)
- [SE-0077: Improved operator declarations](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0077-operator-precedence.md)
- [Operator Declarations (Swift standard library, Apple)](https://developer.apple.com/documentation/swift/operator-declarations)

Source: https://devdocs10.com/articles/swift/swift-custom-operators  
By Murat Bo · Updated 2026-10-17 · DevDocs10 (https://devdocs10.com)

Swift lets you define your own operators when built-in symbols do not express your domain clearly enough. This article explains how custom operators work, how to declare them safely, and when they are worth using.

> **Quick answer:** In Swift, you can create custom operators with `prefix`, `infix`, or `postfix` declarations, then implement them as functions. For infix operators, you should also assign a precedence group, which sets precedence and associativity. Without one, the operator gets `DefaultPrecedence`, and mixing it with other operators or chaining it fails to compile unless you add parentheses.

> **Difficulty:** Advanced
>
> **Helpful to know first:** You'll understand this better if you already know Swift functions, value types, and how built-in operators like `+` and `==` behave.

## 1. What Is Swift Custom Operators?

Custom operators are user-defined symbols that behave like Swift operators. They let you express a specific operation with concise syntax, such as domain math, parsing helpers, or special collection logic.

- They can be `prefix`, `infix`, or `postfix`.
- They are implemented with normal Swift functions.
- Infix operators can be assigned a precedence group that sets their precedence and associativity; without one they use `DefaultPrecedence`.
- They are part of the language, not a framework feature.

Swift also supports operator overloading for existing operators, but custom operators go one step further by letting you introduce a new symbol entirely.

## 2. Why Swift Custom Operators Matter

Custom operators matter when your code benefits from concise, readable notation for a specific domain. They are common in libraries that model math, graphics, parsing, or composable transformations.

They are less useful for everyday application logic where explicit method names are often clearer. A custom operator should improve meaning, not hide it.

## 3. Basic Syntax

Swift custom operators have two parts: a declaration and an implementation.

### Declare the operator

First, tell Swift that a new symbol exists. For an infix operator, you also choose a precedence group.

```swift
precedencegroup VectorAdditionPrecedence {
    associativity: left
    higherThan: AdditionPrecedence
}

infix operator +~: VectorAdditionPrecedence
```

The precedence group tells Swift how to group expressions that mix your operator with others. The operator declaration makes the symbol available to the compiler.

### Implement the operator

Then write a function with the same symbol as its name.

```swift
struct Vector2D {
    var x: Double
    var y: Double
}

func +~ (lhs: Vector2D, rhs: Vector2D) -> Vector2D {
    return Vector2D(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
}
```

Now the operator can be used just like a built-in symbol.

## 4. Step-by-Step Examples

### Example 1: A custom infix operator for vectors

This example adds two vectors using a domain-specific symbol. It is useful when the meaning is obvious in the context of geometry or simulation code.

```swift
struct Vector2D {
    var x: Double
    var y: Double
}

precedencegroup VectorAdditionPrecedence {
    associativity: left
    higherThan: AdditionPrecedence
}

infix operator +~: VectorAdditionPrecedence

func +~ (lhs: Vector2D, rhs: Vector2D) -> Vector2D {
    Vector2D(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
}

let start = Vector2D(x: 2, y: 3)
let delta = Vector2D(x: 5, y: 1)
let result = start +~ delta
```

This code creates a readable expression for vector addition. The result is a new vector with each component added independently.

### Example 2: A prefix operator for unary negation

Prefix operators appear before their operand. They are good for unary operations such as sign inversion or custom transformations.

```swift
prefix operator ~>

prefix func ~> (value: Int) -> Int {
    -value
}

let original = 12
let flipped = ~>original
```

This prefix operator returns the negative of an integer. Even though this example is simple, the same pattern works for more specialized unary behavior.

### Example 3: A postfix operator for a custom check

Postfix operators come after the operand. They are less common, but Swift supports them when they make the domain clearer.

```swift
postfix operator %?

postfix func %? (value: Int) -> Bool {
    value % 2 == 0
}

let isEven = 8%?
```

Here the operator returns `true` for even numbers. The symbol cannot be a bare `?`: Swift disallows postfix operators that start with `?` or `!` because they would collide with optional unwrapping. Even when an operator like this compiles, it is a good reminder that not every clever operator is a good API choice.

### Example 4: A generic custom operator constrained to a protocol

Custom operators are especially useful when multiple types can support the same behavior. A generic operator function constrained to a protocol can centralize the implementation.

```swift
protocol Scalable {
    func scaled(by factor: Double) -> Self
}

prefix operator ^^

prefix func ^^ <T: Scalable> (value: T) -> T {
    value.scaled(by: 2)
}

struct Money: Scalable {
    var amount: Double

    func scaled(by factor: Double) -> Money {
        Money(amount: amount * factor)
    }
}

let salary = Money(amount: 1200)
let doubleSalary = ^^salary
```

This example shows that operators can be generic and reusable, not just attached to one concrete type.

## 5. Practical Use Cases

- Vector, matrix, and geometry code where symbolic notation mirrors the underlying math.
- Parsing and transformation pipelines where a short symbol represents a domain-specific operation.
- State combination or merging logic in libraries that want compact expressions.
- Functional-style APIs that compose transformations in a concise way.
- DSL-like code where a symbol makes a custom meaning easier to scan than a long method name.

For application UI code, a normal method like `merge(with:)` or `clamped(to:)` is often easier to maintain than a custom operator.

## 6. Common Mistakes

### Mistake 1: Declaring an operator but forgetting its implementation

Declaring the symbol is not enough. Swift still needs a function body that tells it what the operator does.

**Problem:** The compiler knows the operator exists, but there is no matching function to execute when you use it.

```swift
infix operator +~

let a = 1
let b = 2
let sum = a +~ b
```

**Fix:** Add the operator function with matching parameter types.

```swift
infix operator +~

func +~ (lhs: Int, rhs: Int) -> Int {
    lhs + rhs
}

let sum = 1 +~ 2
```

The corrected version works because Swift can now resolve the symbol to an actual function.

### Mistake 2: Using an infix operator without a precedence group

Infix operators need precedence rules so Swift can parse expressions consistently. Without them, expressions can become ambiguous or fail to compile.

**Problem:** Swift cannot safely determine how your custom operator should group with other operators.

```swift
infix operator <~>

func <~> (lhs: Int, rhs: Int) -> Int {
    lhs + rhs
}

let value = 2 <~> 3 + 4
```

**Fix:** Define a precedence group and assign the operator to it.

```swift
precedencegroup CustomMathPrecedence {
    associativity: left
    higherThan: AdditionPrecedence
}

infix operator <~>: CustomMathPrecedence

func <~> (lhs: Int, rhs: Int) -> Int {
    lhs + rhs
}

let value = 2 <~> 3 + 4
```

The corrected version gives Swift enough information to parse the expression the way you intend.

### Mistake 3: Choosing a symbol that makes the code unclear

A custom operator can technically compile and still be a poor design choice if nobody can read it later.

**Problem:** The operator hides intent, so the code becomes harder to understand and maintain than a named method would be.

```swift
infix operator %%

func %% (lhs: String, rhs: String) -> String {
    lhs + " " + rhs
}

let message = "Hello" %% "world"
```

**Fix:** Use a descriptive method when the symbol does not add real clarity.

```swift
extension String {
    func joinedWithSpace(_ other: String) -> String {
        self + " " + other
    }
}

let message = "Hello".joinedWithSpace("world")
```

The corrected version is easier to read because the intent is explicit instead of symbolic.

## 7. Best Practices

### Use operators only when the domain already suggests symbolic notation

If the operation looks mathematical or strongly compositional, a custom operator can be a good fit. Otherwise, prefer a named function.

```swift
// Good fit: expression mirrors the domain.
let distance = pointA +~ pointB

// Often clearer as a named function.
let formatted = formatTitle(text)
```

This keeps the API readable for future contributors.

### Choose a precedence group deliberately

Do not rely on guesswork. Decide how your operator interacts with arithmetic, comparison, or logical expressions.

```swift
precedencegroup PipelinePrecedence {
    associativity: left
    higherThan: AdditionPrecedence
}

infix operator |>: PipelinePrecedence
```

Clear precedence avoids surprising grouping in complex expressions.

### Keep operator behavior small and predictable

An operator should do one obvious thing. If it performs multiple steps, hides side effects, or depends on global state, it becomes hard to trust.

```swift
// Prefer a simple, deterministic operation.
func +~ (lhs: Vector2D, rhs: Vector2D) -> Vector2D {
    Vector2D(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
}
```

Predictable behavior makes the operator safe to reuse in many contexts.

## 8. Limitations and Edge Cases

- Custom operators can make code harder to understand if the symbol is not obvious.
- They are available to all code in scope, so naming collisions can happen in large codebases.
- Infix operators without a precedence group fall back to `DefaultPrecedence`, which is non-associative and unordered relative to arithmetic and comparison operators, so mixing or chaining them requires parentheses. Prefix and postfix operators cannot declare a precedence group at all.
- You cannot redefine how Swift parses the operator beyond the precedence system.
- Operators may compile but still create confusing APIs when used in public libraries.

**Warning:** Be careful exposing custom operators in shared frameworks. Once an operator becomes part of a public API, removing or renaming it is a breaking change for downstream users.

## 9. Practical Mini Project

Let us build a tiny geometry helper that uses a custom operator to translate a point by a vector. This example shows the full flow from declaration to use.

```swift
struct Point {
    var x: Double
    var y: Double
}

struct Offset {
    var dx: Double
    var dy: Double
}

precedencegroup TranslationPrecedence {
    associativity: left
    higherThan: AdditionPrecedence
}

infix operator >+<: TranslationPrecedence

func >+< (point: Point, offset: Offset) -> Point {
    Point(x: point.x + offset.dx, y: point.y + offset.dy)
}

let origin = Point(x: 10, y: 20)
let shift = Offset(dx: 3, dy: -4)
let moved = origin >+< shift
```

In this mini project, the operator makes the operation read like a compact domain statement. The complete example works because the type declarations, operator declaration, and operator function all match.

## 10. Key Points

- Custom operators let you create new symbols for domain-specific behavior.
- Swift supports prefix, infix, and postfix operators.
- Every operator needs an implementation. Give infix operators a precedence group as well, because without one they cannot be mixed with other operators or chained without parentheses.
- Use them only when the symbol improves readability.
- Public operators should be designed carefully because they become part of your API surface.

## 11. Practice Exercise

- Create a `Fraction` type with numerator and denominator.
- Define a custom infix operator that adds two fractions.
- Choose a precedence group so the operator behaves predictably in expressions.
- Test it with two sample values and print the result.

**Expected output:** A new fraction representing the sum of the two inputs.

**Hint:** Start by implementing fraction addition with a normal function, then move the same logic into a custom operator.

```swift
struct Fraction {
    var numerator: Int
    var denominator: Int
}

precedencegroup FractionAdditionPrecedence {
    associativity: left
    higherThan: AdditionPrecedence
}

infix operator +÷: FractionAdditionPrecedence

func +÷ (lhs: Fraction, rhs: Fraction) -> Fraction {
    let numerator = lhs.numerator * rhs.denominator + rhs.numerator * lhs.denominator
    let denominator = lhs.denominator * rhs.denominator
    return Fraction(numerator: numerator, denominator: denominator)
}

let first = Fraction(numerator: 1, denominator: 2)
let second = Fraction(numerator: 1, denominator: 3)
let total = first +÷ second
print(total)
```

Output (tested with Swift 6.4):

```text
Fraction(numerator: 5, denominator: 6)
```

## 12. Final Summary

Swift custom operators let you introduce your own symbols for operations that fit your domain better than a long method name. They are powerful because they integrate into Swift syntax, but that same power makes them easy to overuse.

The safest approach is to reserve custom operators for code where the symbol is immediately meaningful, the behavior is small and predictable, and the precedence rules are clear. When those conditions are met, operators can make a specialized API elegant and expressive.

If you want to go further, the next best step is to study operator overloading on existing symbols such as `+`, `==`, and `<` so you can compare when a custom symbol is better than extending a familiar one.

## Where AI Coding Assistants Get This Wrong

- Often suggested: Emits pre-Swift 3 syntax such as `infix operator ** { associativity right precedence 160 }`, or capitalized `associativity: Left`.
  Instead: Declare a `precedencegroup` with lowercase `associativity: right` and `higherThan:` / `lowerThan:` relations, then write `infix operator **: ExponentiationPrecedence`. (Swift 6.4 rejects both: "operator should no longer be declared with body; use a precedence group instead" and "expected 'none', 'left', or 'right' after 'associativity'".)
- Often suggested: Writes `public infix operator ...` in a library, or declares `postfix operator ?` or `prefix operator &`.
  Instead: Declare the operator without an access modifier and mark the implementing `static func` or global `func` `public`; pick a symbol that doesn't collide with `?`, `!`, `&`, `=` or `->`. (These are compile errors ("unexpected attribute 'public' in operator declaration", "postfix operator names starting with '?' or '!' are disallowed"), and operator declarations are already visible to every importing module.)
- Often suggested: Defines `**` with `MultiplicationPrecedence` or a left-associative custom group.
  Instead: Give exponentiation its own group with `associativity: right` and `higherThan: MultiplicationPrecedence`. (Only that combination gives the mathematical results `2 ** 3 ** 2 == 512` and `2 * 3 ** 2 == 18` (checked with Swift 6.4).)
