# Swift Existentials (any) and Self Requirements Explained

Source: https://devdocs10.com/articles/swift/swift-existentials-any-self  
By Murat Bo · Published 2026-05-28 · DevDocs10 (https://devdocs10.com)

Swift’s `any` keyword lets you store values behind a protocol type, while `Self` requirements limit which protocols can be used that way. This article explains what existential types are, why some protocols cannot be used as values, and how to recognize and fix the compiler errors that appear when protocol requirements depend on the conforming type itself.

> **Quick answer:** Use `any Protocol` when you need a value of unknown concrete type that still conforms to a protocol. If a protocol mentions `Self` in a requirement, Swift usually cannot treat it as an existential because the protocol needs to know the exact conforming type.

> **Difficulty:** Intermediate
>
> **You'll understand this better if you know:** basic Swift protocols, generic functions, and the difference between a concrete type like `String` and a protocol like `Collection`.

## 1. What Are Existentials and Self Requirements?

An existential is a value that hides its concrete type behind a protocol interface. You use it when the exact type does not matter, only the behavior described by the protocol does.

- `any` marks an existential type explicitly.
- The stored value can be any concrete type that conforms to the protocol.
- You can call protocol requirements through the existential value.
- Some protocols cannot be used as existentials if they include `Self` requirements or associated types.

A `Self` requirement means the protocol refers to the conforming type itself. That creates a dependency on the exact concrete type, which often prevents Swift from erasing the type into a plain protocol value.

## 2. Why Existentials Matter

Existentials are useful whenever you want flexibility at runtime. They are common in APIs that collect unrelated conforming types into one container or pass around values where the concrete type is intentionally hidden.

They matter because they make protocols usable as values, not just as generic constraints. At the same time, they introduce tradeoffs: dynamic dispatch, less type information, and restrictions when protocols depend on `Self`.

## 3. Basic Syntax

The core syntax is simple. Add `any` before the protocol name when you want an existential value.

### Minimal example

This example stores a value in a protocol-typed variable and calls a protocol method through it.

```swift
protocol Drawable {
    func draw()
}

struct Circle: Drawable {
    func draw() {
        print("Drawing a circle")
    }
}

let shape: any Drawable = Circle()
shape.draw()
```

Output (tested with Swift 6.4):

```text
Drawing a circle
```

Here, `shape` can hold any type that conforms to `Drawable`. Swift knows it has a `draw()` method, but it does not keep the concrete type in the variable’s static type.

## 4. Step-by-Step Examples

### Example 1: Storing different conforming values in one array

Existentials are handy when you want one collection to hold multiple concrete types that share the same protocol.

```swift
protocol Logger {
    func log(_ message: String)
}

struct ConsoleLogger: Logger {
    func log(_ message: String) {
        print("Console: \(message)")
    }
}

struct FileLogger: Logger {
    func log(_ message: String) {
        print("File: \(message)")
    }
}

let loggers: [any Logger] = [ConsoleLogger(), FileLogger()]

for logger in loggers {
    logger.log("App started")
}
```

Output (tested with Swift 6.4):

```text
Console: App started
File: App started
```

This works because both types conform to `Logger`. The array stores existential values, not a single shared concrete type.

### Example 2: A protocol that cannot be used as an existential

Protocols with `Self`-based requirements often need the exact conforming type. That is where Swift draws a line.

```swift
protocol Clonable {
    func clone() -> Self
}

struct Token: Clonable {
    func clone() -> Token {
        Token()
    }
}

let value: any Clonable = Token()
```

This is the kind of declaration that can fail or become unusable depending on the protocol’s exact requirements. If a protocol returns `Self` or compares `Self` values, Swift must preserve the concrete type relationship.

### Example 3: Using an existential in a function parameter

You can accept an existential when your function only needs the protocol behavior and does not care about the concrete type.

```swift
protocol Greeter {
    func greet() -> String
}

func welcome(guest: any Greeter) {
    print(guest.greet())
}

struct Person: Greeter {
    func greet() -> String {
        "Hello"
    }
}

welcome(guest: Person())
```

Output (tested with Swift 6.4):

```text
Hello
```

This is useful when the function is a consumer of behavior, not a transformer of the concrete type.

### Example 4: A generic function versus an existential parameter

Generics preserve the concrete type, while existentials hide it. That difference matters when you need to return or transform the same type.

```swift
protocol IdentifiableName {
    var name: String { get }
}

struct User: IdentifiableName {
    let name: String
}

func show<T: IdentifiableName>(item: T) -> T {
    print(item.name)
    return item
}

func showAny(item: any IdentifiableName) {
    print(item.name)
}
```

The generic version keeps the exact type and can return it. The existential version can use the interface, but it cannot preserve the original type in the same way.

## 5. Practical Use Cases

- Storing heterogeneous objects that share the same protocol, such as different formatters, loggers, or command handlers.
- Accepting values from APIs where the caller chooses the concrete type, but your function only needs protocol behavior.
- Building plugin-like systems where each module provides a different implementation of the same capability.
- Hiding implementation details while exposing a narrow API surface.
- Working with protocols whose requirements do not depend on `Self` or associated types.

## 6. Common Mistakes

### Mistake 1: Forgetting to write `any` for a protocol value

Swift requires `any` when you mean an existential. Older code or tutorials may omit it, but modern Swift makes the distinction explicit.

**Problem:** The compiler may report that a protocol can only be used as a generic constraint, because the protocol is being written where a value type is expected.

```swift
protocol Shape {
    func area() -> Double
}

let item: Shape = Circle()
```

**Fix:** Mark the protocol type with `any`.

```swift
let item: any Shape = Circle()
```

The corrected version works because Swift now knows you want an existential value.

### Mistake 2: Expecting an existential to preserve the concrete type

An existential hides the real type, so you cannot use it the same way as a generic value.

**Problem:** Code that tries to return the exact stored type or use type-specific members will fail because the compiler only knows the protocol interface.

```swift
protocol Named {
    var name: String { get }
}

func duplicate(value: any Named) -> any Named {
    return value
}
```

**Fix:** Use a generic function when you need the input and output to stay the same concrete type.

```swift
func duplicate<T: Named>(value: T) -> T {
    return value
}
```

The generic version preserves the concrete type, which is what the existential version cannot do.

### Mistake 3: Using a protocol with `Self` requirements as a plain value type

Protocols that mention `Self` often cannot be turned into regular existential values. This is common with comparison, cloning, or chaining APIs.

**Problem:** Swift may produce an error such as “protocol can only be used as a generic constraint because it has Self or associated type requirements.” That means the protocol needs more type information than an existential can provide.

```swift
protocol Combining {
    func combine(_ other: Self) -> Self
}

let value: any Combining = Example()
```

**Fix:** Use a generic parameter so Swift can keep track of the exact conforming type.

```swift
func merge<T: Combining>(left: T, right: T) -> T {
    return left.combine(right)
}
```

The generic version succeeds because both parameters and the return value use the same concrete type.

## 7. Best Practices

### Prefer generics when the concrete type must stay visible

If your function needs to preserve type identity, return the same type, or call requirements that depend on `Self`, a generic is usually better than an existential.

```swift
func render<T: Drawable>(value: T) {
    value.draw()
}
```

This keeps compile-time type information intact and avoids many existential limitations.

### Use `any` when you really need heterogeneity

If a collection or property must hold different concrete types, an existential is the right tool.

```swift
let queue: [any Logger] = [ConsoleLogger(), FileLogger()]
```

This is clearer than trying to force unrelated types into one generic container.

### Keep protocol APIs existential-friendly when possible

If you design a protocol that should be easy to store or pass around as a value, avoid unnecessary `Self` requirements and associated types.

```swift
protocol PrintableTask {
    var title: String { get }
    func run()
}
```

Designing with existential use in mind makes the protocol more flexible for callers.

## 8. Limitations and Edge Cases

- Existentials hide the concrete type, so you lose some compile-time optimizations and type relationships.
- Methods that return `Self` or take `Self` as an argument often block existential use.
- Protocols with associated types usually need generics or type erasure rather than a simple existential.
- Calling members on an existential can be limited to requirements that are part of the protocol interface.
- Protocol composition like `any P & Q` works only when the combined requirements are still existential-friendly.

> A useful rule: if Swift must know the exact conforming type to check the API, the protocol is probably better used as a generic constraint than as an existential.

## 9. Practical Mini Project

Let’s build a tiny task runner that stores different task types in one list and executes them through a protocol existential.

```swift
protocol Task {
    var title: String { get }
    func run()
}

struct DownloadTask: Task {
    let title = "Download file"
    func run() {
        print("Downloading...")
    }
}

struct BackupTask: Task {
    let title = "Backup database"
    func run() {
        print("Backing up...")
    }
}

let tasks: [any Task] = [
    DownloadTask(),
    BackupTask()
]

for task in tasks {
    print("Running: \(task.title)")
    task.run()
}
```

Output (tested with Swift 6.4):

```text
Running: Download file
Downloading...
Running: Backup database
Backing up...
```

This project shows a common existential pattern: the runner only cares that each item can be titled and executed. The concrete task type stays hidden inside the array.

## 10. Key Points

- `any` creates an existential value from a protocol.
- Existentials are useful when you need a value that can hold multiple concrete conforming types.
- `Self` requirements often prevent a protocol from being used as a plain existential.
- Generics preserve concrete type information; existentials trade that away for flexibility.
- If a protocol needs the exact type to work correctly, prefer generics or redesign the protocol.

## 11. Practice Exercise

Create a protocol-based notification system that accepts different message senders in one array. Make sure each sender can provide a label and send a message.

- Define a protocol with two requirements: a read-only label and a send method.
- Create two or more concrete types that conform to the protocol.
- Store them in an array using `any`.
- Loop over the array and call both requirements.

**Expected output:** Each sender’s label and message should print in order.

**Hint:** Use only requirements that do not depend on `Self`, so the protocol stays existential-friendly.

**Solution:**

```swift
protocol Sender {
    var label: String { get }
    func send()
}

struct EmailSender: Sender {
    let label = "Email"
    func send() {
        print("Sending email")
    }
}

struct SMSSender: Sender {
    let label = "SMS"
    func send() {
        print("Sending SMS")
    }
}

let senders: [any Sender] = [EmailSender(), SMSSender()]

for sender in senders {
    print(sender.label)
    sender.send()
}
```

Output (tested with Swift 6.4):

```text
Email
Sending email
SMS
Sending SMS
```

This solution works because the protocol describes behavior that can be used through an existential without needing the concrete type itself.

## 12. Final Summary

Swift existentials let you treat a protocol as a value by writing `any Protocol`. That is useful when you need flexibility and do not care which concrete type is stored, as long as it conforms to the protocol.

Protocols with `Self` requirements are different. They need the exact conforming type to preserve type relationships, so they often cannot be used as simple existential values. In those cases, generics are usually the better fit, or the protocol may need to be redesigned to avoid `Self`-dependent requirements.

If you remember one rule, make it this: use `any` for protocol values that only need protocol behavior, and use generics when the exact concrete type matters. The more you practice switching between the two, the easier Swift’s type system becomes to read and design for.
