Nested generic types not allowed?

I'm implementing a simple linked-list stack.

I can get the following Int-only implementaion to work just fine:


class LinkedListIntStack {
    class Node {
        let item: Int
        var next: Node?
        init(item: Int) {
            self.item = item
        }
    }

    /* ... implementation omitted for brevity ... */
}


Unfortunately, I can't get the same nested construct to work with a generic type:


class LinkedListStack<T> {
    class Node<T> {
        let item: T
        var next: Node?
        init(item: T) {
            self.item = item
        }
    }


   /* ... omitted for brevity ... */
}


The above fails to compile with the error message:


error: generic type 'Node' nested in type 'LinkedListStack' is not allowed


I can work around this by moving Node<T> outside of LinkedListStack<T>.

However, I find the nested type solution more elegant.


My questions:

  1. Is it wrong for me to expect this to work? Is there a mistake I'm making and not realizing?
  2. Is this a design decision or just a current limitation of Swift? If it's the former, what is the motivation behind it?
  3. Are there alternative solutions you can recommend?


Thanks!

Replies

Just as a complementary note, generic types aren't allowed to have any nested types (no matter if the nested type is generic or not):

struct Foo<T> {
    struct Bar { // Error: Type 'Bar' nested in generic type 'Foo' is not allowed
    }
}

Generic types are allowed to have associated types. So, it is actually possible to define something like this:


class LinkedListStack<T> {
  private typealias Node = (item: T, next: AnyObject)
  ...
}


Unfortunately,

Node
is a recursive data structure which cannot be encoded as a tuple even if the recursion is indirect. So, it won't be possible to replace
AnyObject
with
Node?
.

Late to the party. Unfortunately this is a limitation in Swift 2.2 and below as it is available in Swift 3.

The limitation is explained here - https://devforums.apple.com/message/1046539#1046539

"It's an implementation limitation. We'll remove the restriction once our compiler and runtime are able to correctly handle types nested in generic contexts." - jckarter (In response to racoiaws on Sep 20, 2014 9:51 AM)


As a workaround you can just create the node outside of the class.


class Node<T> {
     let item: T
}

class LinkedListStack<T> {
     var head: Node<T>?
}


This also work with protocols on the generic types as long as they both implement the same protocols.


class Node<T: Equatable> {
     let item: T
}
class LinkedListStack<T: Equatable> {
     var head: Node<T>?
}

So this is available in swift 3? I get the same error is swift 3 - is there some special syntax for it?

me too, with Xcode 8.1.

gosh, I'm so eager to implement all the fantastic swift3.0 features, while this issue really upset me.

My code will look bitterly ungly if i move the nested type outside.😟

The Generics Manifesto still lists this as not supported.

My code will look bitterly ungly if i move the nested type outside.

Which specific thing do you find ungly [sic :-]? Personally I just move my type up to the top level, set up a type alias, and move on.

struct LinkedListStack<T> { 
    private typealias Node = ListNode<T>
    private var items: [Node] = []
    …
}

fileprivate struct ListNode<T> {
    …
}

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I stumbled above the same limitation. The "ugliness" here is, that you need to _prefix_ the name of the nested class in order to avoid name clashes.


IMHO, the support of nested types (with or without generics) leaves much to be desired.

The "ugliness" here is, that you need to prefix the name of the nested class in order to avoid name clashes.

Sure, but if you always refer to it by its typealias that’s a small bit of boilerplate that you can write and then try to forget about.

I’m not arguing that the current behaviour is ideal, just that I don’t lose a lot sleep about it, especially compared to some of the other items listed in the Generics Manifesto.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

The code snippet in the question compiles in Swift 5.4.