54

To start off, I want to say that I'm aware there are many articles and questions within SO that refer to the indirect keyword in Swift.

The most popular explanation for the usage of indirect is to allow for recursive enums.

Rather than just knowing about what indirect allows us to do, I would like to know how it allows us to use recursive enums.

Questions:

Is it because enums are value types and value types do not scale well if they are built in a recursive structure? Why?

Does indirect modify the value type behaviour to behave more like a reference type?

The following two examples compile just fine. What is the difference?

indirect enum BinaryTree<T> {
  case node(BinaryTree<T>, T, BinaryTree<T>)
  case empty
}

enum BinaryTree<T> {
  indirect case node(BinaryTree<T>, T, BinaryTree<T>)
  case empty
}
Kelvin Lau
  • 6,373
  • 6
  • 34
  • 57

3 Answers3

56

The indirect keyword introduces a layer of indirection behind the scenes.

You indicate that an enumeration case is recursive by writing indirect before it, which tells the compiler to insert the necessary layer of indirection.

From here

The important part of structs and enums is that they're a constant size. Allowing recursive structs or enums directly would violate this, as there would be an indeterminable number of recursions, hence making the size non constant and unpredictable. indirect uses a constant size reference to refer to a constant size enum instance.

There's a different between the two code snippets you show.

  1. The first piece of code makes BinaryTree<T> stored by a reference everywhere it's used.

  2. The second piece of code makes BinaryTree<T> stored by a reference only in the case of node. I.e. BinaryTree<T> generally has its value stored directly, except for this explicitly indirect node case.

Alexander
  • 59,041
  • 12
  • 98
  • 151
  • 4
    So does that mean an `indirect` struct or enum should be considered as reference types? Does this mean they now have reference semantics and no longer copy on write? – Kelvin Lau May 30 '16 at 20:58
  • 2
    I'm not sure, you'll have to test it. IIRC, `indirect` isn't allowed on structs – Alexander May 30 '16 at 21:20
  • Thanks. And you're right, `indirect` isn't allowed on structs. – Kelvin Lau May 30 '16 at 21:22
  • 4
    Which kind of makes sense. At that point, they'd just be classes. – Alexander May 30 '16 at 21:24
  • 3
    Just did some tests. It seems like `indirect` enums still have the copy-on-write attribute despite the extra layer of indirection. This brings me to ask the question: What exactly changed behind the scenes? – Kelvin Lau May 30 '16 at 22:27
  • I do not think `indirect` will create a reference for `enum`, because assigning it to a new variable and changing it, will not change the old one. – Binarian Feb 03 '17 at 10:29
  • @iGodric theres nothing stopping the compiler from emulating value semantics just because the values happen to be indirect via with references – Alexander Feb 03 '17 at 16:35
  • @Alexander You are right, but we do not know and I think from a high level perspective it is still value semantics. Your answer could be irritating, because it is just an implementation detail. You are saying it is reference semantics which is misleading to reference type. – Binarian Feb 03 '17 at 16:40
  • 1
    @iGodric The very nature of the question revolves around implementation details. That said, I'll change my first sentence – Alexander Feb 03 '17 at 16:44
0

Swift indirect enum

Since Swift v2.0

Swift Enum[About] is a value type[About], and we assign it the value is copied that is why the size of type should be calculated at compile time.

Problem with associated value

enum MyEnum { //Recursive enum <enum_name> is not marked 
    case case1(MyEnum) `indirect`
}

it is not possible to calculate the final size because of recursion

Indirect says to compiler to store the associated value indirectly - by reference(instead of value)

  • indirect enum - is stored as reference for all cases
  • indirect case - is stored as reference only for this case

Also indirect is not applied for other value types(struct)

yoAlex5
  • 29,217
  • 8
  • 193
  • 205
0

You can use indirect enum. It's not exactly struct, but it is also a value type. I don't think struct has similar indirect keyword support.

From Hacking with Swift post:

Indirect enums are enums that need to reference themselves somehow, and are called “indirect” because they modify the way Swift stores them so they can grow to any size. Without the indirection, any enum that referenced itself could potentially become infinitely sized: it could contain itself again and again, which wouldn’t be possible.

As an example, here’s an indirect enum that defines a node in a linked list:

indirect enum LinkedListItem<T> {
    case endPoint(value: T)
    case linkNode(value: T, next: LinkedListItem)
}

Because that references itself – because one of the associated values is itself a linked list item – we need to mark the enum as being indirect.

Hlung
  • 13,850
  • 6
  • 71
  • 90