Antonio
Antonio

Reputation: 72760

Compilation error with generics and closure to initialize a class instance

What I am trying to do is to inject a closure into a class initializer so that I can control its initialization from outside.

I've defined a protocol which must be implemented by the classes I want to initialize:

protocol EntityInitializable {
    typealias T : EntityInitializable
    init(initializer: (T) -> ())
}

and a function attempting to inject the closure

class EntityMapper<T : EntityInitializable> {
    func f() -> T {
        var cb = {(entity: T) -> () in
        }

        return T(cb)
    }
}

This generates the following error during compilation:

error: cannot convert the expression's type 'T.T' to type 'T' return T(cb)

If I modify the above code removing the generics part, such as:

class MyClass {
    init(initializer: (MyClass) -> ()) {
    }
}

class EntityMapper {
    func f() -> MyClass {
        var cb = {(entity: MyClass) -> () in
        }

        return MyClass(cb)
    }
}

it works.

Any idea about what's wrong? Is it perhaps because I am trying to make recursive use of generics, by creating an instance of a generic and passing a closure with a parameter of the same generic type?

Upvotes: 0

Views: 248

Answers (1)

EliaCereda
EliaCereda

Reputation: 2490

The reason that your code doesn't work is that there is nothing that says the T in EntityInitializable (for clarity I'll call it EntityType from here on) must be equal to the type that implements the protocol. Here's an example that should shed some light on the issue:

class A: EntityInitializable {
    typealias EntityType = B
    init(initializer: (EntityType) -> ()) {

    }
}

class B: EntityInitializable {
    // [...]
}

This is perfectly fine according to the code you wrote and it's the reason why the compiler doesn't accept the cb closure. Its entity parameter has type T, while EntityInitializable expect its type to be T.EntityType.

The right way to write this code is by using Self which represents the class that implements the protocol:

protocol EntityInitializable {
    init(initializer: (Self) -> ())
}

Upvotes: 1

Related Questions