2

Given a protocol without any funk whatsoever:

protocol NonFunkyProtocol {}

And a protocol with some serious funk going on:

protocol FunkyProtocol {
    func funky<T: NonFunkyProtocol>(_ closure: (T) -> Void)
}

Then given this struct:

struct WeeStruct: FunkyProtocol {
    let weeProp: NonFunkyProtocol
    
    func funky<T>(_ closure: (T) -> Void) where T: NonFunkyProtocol {
        closure(weeProp)
    }
}

I would expect this to compile as the type of the parameter expected in closure is T where T conforms to NonFunkyProtocol, and weeProp is of type NonFunkyProtocol.

Instead I'm seeing this error:

error

I've more than likely a hole somewhere in my generics knowledge, where am I going wrong?

Community
  • 1
  • 1
mike
  • 2,073
  • 4
  • 20
  • 31

1 Answers1

4

The problem is that T in this case is "some type that conforms to NonFunkyProtocol". weeProp is also "something that conforms to NonFunkyProtocol" but there's nothing that says that weeProp is of type T.

Consider the following case:

extension Int: NonFunkyProtocol {}
extension String: NonFunkyProtocol {}

Both Int and String conform.

Now I construct a WeeStruct with a String:

let wee = WeeStruct(weeProp: "")

And I call funky with a function that requires an Int (since Int a conforming type, it can be T):

wee.funky { (int: Int) -> Void in print(int + 1) }

So this would pass "" to the closure. How can that work?

So you either need to require the closure handle any NonFunkyProtocol (I strongly suspect this is what you mean):

func funky(_ closure: (NonFunkyProtocol) -> Void)

Or you need to nail down weeProp to T by making T an associatedtype:

protocol FunkyProtocol {
    associatedtype T: NonFunkyProtocol
    func funky(_ closure: (T) -> Void)
}

struct WeeStruct<T:NonFunkyProtocol>: FunkyProtocol {
    let weeProp: T

    func funky(_ closure: (T) -> Void) {
        closure(weeProp)
    }
}

I would be very careful before adding an associatedtype, though. That completely changes the nature of FunkyProtocol.

If FunkyProtocol really is just this one requirement, you should also ask what it's solving versus just a function. Why pass around WeeStruct with all its protocol baggage, when you could just use the wee.funky function directly? Are there protocol extension on FunkyProtocol? If you can't write generic algorithms against FunkyProtocol, it probably should't be a protocol.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • 1
    Lightbulb moment! Thanks very much, in this particular example you're correct it shouldn't be a protocol but it was just a very distilled example to try and wrap my head around a problem I was facing in one of my projects. – mike Mar 22 '19 at 14:51