Search code examples
swiftprotocols

Referencing instance method 'stringify()' on 'Collection' requires the types 'Int' and 'Stringify' be equivalent


I made an protocol Stringify to convert Types that implement the protocol to a String.

protocol Stringify {
    func stringify() -> String
}

extension Collection where Iterator.Element == Stringify {
    /// changes all the elements in the collection to a String
    func stringify() -> [String] {
        var strings = [String]()
        if let elements = self as? [Stringify] {
            for element in elements {
                strings.append(element.stringify())
            }
        }
        return strings
    }
}

extension Int: Stringify {
    func stringify() -> String {
        return String(self)
    }
}

extension Double: Stringify {
    func stringify() -> String {
        return String(self)
    }
}


let test = [5,6,7]

/// does work
[6,5,34].stringify()


/// does not work -> Error alert
test.stringify()

But when I set a collection of Ints to a property and using then stringify() on it, it does not work.

Error:

Referencing instance method 'stringify()' on 'Collection' requires the types 'Int' and 'Stringify' be equivalent

If I use it directly everything is going fine.

What is the problem here?


Solution

  • extension Collection where Iterator.Element == Stringify 
    

    has a “same type requirement” and defines an extension for collections whose elements are of the type Stringify. But test is an array of Int, i.e. the elements conform to the Stringify protocol. So what you want is

    extension Collection where Iterator.Element : Stringify
    

    or, equivalently,

    extension Collection where Element : Stringify
    

    The reason that

    /// does work
    [6,5,34].stringify()
    

    compiles with your original definition is that the compiler infers the type of the array as [Stringify] from the context.

    let test: [Stringify] = [5,6,7]
    test.stringify()
    

    would compile as well.


    Note that there is no need to cast self in the extension method. You can simplify the implementation to

    func stringify() -> [String] {
        var strings = [String]()
        for element in self {
            strings.append(element.stringify())
        }
        return strings
    }
    

    or just

    func stringify() -> [String] {
        return self.map { $0.stringify() }
    }