Swift: Protocol requirements

Swift 2.0 enables you to add public extensions of generic types. In Beta 2, extensions can be constrained by adding  protocol requirements on the type parameters of the generic type.

Last night, Gal3rielol asked if he could use extensions to add methods to arrays with restricted types, such as [Int] or [MyType].

You cannot add specific typing, like

extension Array where Element == Int

because this type requirement transforms the generic Array into a non-generic version.

Nor, can you perform an extension on Array<Int>:

error: constrained extension must be declared on the 
unspecialized generic type 'Array' with constraints 
specified by a 'where' clause

You can find or create a protocol that limits your extension to more or less just the type you want to work with:

extension Array where T : StringLiteralConvertible 
extension Array where T : IntegerArithmeticType

Or create a custom protocol and use it restrict the items your extension works with:

protocol OnlyStrings {}
extension String: OnlyStrings{}
extension Array where T : OnlyStrings

This approach is limited. While you know the extension only applies to strings, the compiler is still working with type T. This example by Mike Ash demonstrates the issue:

extension Array where T : OnlyStrings {
    func appendAll() -> String {
        var accumulator = ""
        for item in self { accumulator += item }
        return accumulator
    }
}

This errors with “error: binary operator ‘+=’ cannot be applied to operands of type ‘String’ and ‘T’” Work around this by casting item to String(item). You should also add a StringLiteralConvertible restriction. It’s inelegant at best.

3 Comments

  • > This errors with “error: binary operator ‘+=’ cannot be applied to operands of type ‘String’ and ‘T’”
    if let item = item as? T

  • I’m thinking that being able to write T == String (or T: String) and so on will follow in later betas. After all we can already use classes and write things like T: UIView, so why not? But for now how about this as a way to reduce an array of strings into a single string (using an extension):

    —————–
    protocol StringType {
    var characters: String.CharacterView { get }
    }

    extension String:StringType {
    }

    extension Array where T:StringType {
    func appendAll() -> String {
    return String(self.map{$0.characters}.reduce(String.CharacterView(), combine: {$0 + $1}))
    }
    }

    [“Hello “,”Swift”,”!”].appendAll() // Hello Swift!
    ——————–

    • Please forgive the indulgence of responding to my own comment, but it’s worth noting for those who are actually looking to reduce an array of strings into a single string, that this particular example is really an academic exercise because IRL we’d just use one of the pre-existing methods:
      ——-
      “”.join([“one”,”two”,”three”]) // “onetwothree”

      [“one”,”two”,”three”].reduce(“”,combine:{$0 + $1}) // “onetwothree”
      ——-