2

I have a protocol AProtocol that has some data structs and a protocol BProtocol that has an action which takes parameters conform to AProtocol. The code goes like this:

protocol AProtocol {
    // data
}

protocol BProtocol {
    func action<T: AProtocol>(completionHandle: (Bool, [T]?) -> ())
}

When I implement these protocols - A struct conforms to AProtocol and a class conforms to BProtocol, I cannot find a way to satisfy the compiler.

struct AStruct: AProtocol {

}

class BClass: BProtocol {
    var structs = [AStruct]()
    func action<T : AProtocol>(completionHandle: (Bool, [T]?) -> ()) {
        completionHandle(true, self.structs) // Compile error: "'AStruct' is not identical to 'T'"
    }
}

Updated:

I tried using type casting, but failed to invoke the action with another error("Cannot convert the expression's type '(($T4, ($T4, $T5) -> ($T4, $T5) -> $T3) -> ($T4, ($T4, $T5) -> $T3) -> $T3, (($T4, $T5) -> ($T4, $T5) -> $T3, $T5) -> (($T4, $T5) -> $T3, $T5) -> $T3) -> (($T4, ($T4, $T5) -> $T3) -> $T3, (($T4, $T5) -> $T3, $T5) -> $T3) -> $T3' to type 'AProtocol'"):

class BClass: BProtocol {
    var structs = [AStruct]()
    func action<T : AProtocol>(completionHandle: (Bool, [T]?) -> ()) {
        completionHandle(true, self.structs.map({$0 as T})) // Now the compile error has gone
    }

    func testAction() {
        self.action({ // Compile error: "Cannot convert the expression's type..."
            (boolValue, arrayOfStructs) in
            if boolValue {
                // Do something
            }
        })
    }
}

I wonder why am I wrong and how to solve the problem. Thank you!

2 Answers 2

1

You can solve it with

completionHandle(true, self.structs.map { $0 as T })

I am not sure whether its a bug or some constraint in the languages that forbids you to directly cast this array. It could be that its not possible because arrays are value types.

To your updated question: You have specified generic type for action method, so compiler cannot get the type from context. You have to explicitly set it:

var bClass = BClass()
bClass.action { (boolValue, arrayOfAProtocol: [AProtocol]?) in
    if boolValue {
        // Do something
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! It calms down the compiler indeed. But when I try invoking the method action, another error arises and it seems have something to do with the type casting, as shown in my updated question.
There is still a little problem: the bClass.action { (boolValue, arrayOfAProtocol: [AProtocol]?) should be bClass.action { (boolValue, arrayOfAProtocol: [AStruct]?)
1

In order for that to work you have to:

  • change the structs type from [AStruct] to [AProtocol]
  • within the action method, explicitly cast self.structs to [T]

Code:

class BClass: BProtocol {
    var structs = [AProtocol]()
    func action<T : AProtocol>(completionHandle: (Bool, [T]?) -> ()) {
        completionHandle(true,  self.structs as? [T])
    }
}

The reason is that the action method expects any type implementing the AProtocol. By using self.structs, you are forcing it to use AStruct.

Let me make an example. Consider you have this struct:

struct BStruct: AProtocol {}

It implements AProtocol, so as per the method definition you should be able to call action using that as the generic type. But BStruct is not AStruct (although they implement the same protocol), so the compiler doesn't know how to cast [AStruct] to [BStruct], if that's even possible.

Depending from what's the goal of what you're trying to achieve, you could change your protocol a little bit, by moving the generic type T from the method to the protocol/class level:

protocol BProtocol {
    typealias DataType
    func action(completionHandle: (Bool, [DataType]?) -> ())
}

class BClass<T: AProtocol>: BProtocol {
    typealias DataType = T

    var structs = [T]()
    func action(completionHandle: (Bool, [T]?) -> ()) {
        completionHandle(true, self.structs)
    }
}

This ensures that structs contains elements of the same type expected by the action method.

1 Comment

Thank you very much! Actually, I have used the second solution you post before asking the question. However, since I use the AProtocol as an interface data structure, I'd like to put a constraint on the type T to ensure everything communicate with AProtocol. So I tried your first solution and it works like a charm! I would mark your answer as accepted if I could mark both. I vote for it. Thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.