Can't extend a generic type with a non-protocol constraint?

It appears that you can’t extend a generic class/struct with a requirement that a type parameter inherit from a non-protocol:
  extension Dictionary where Key : String { … }
The above produces the error “Type ‘Key’ constrained to non-protocol type ‘String’”.

So is there a way I can add a method to a class, where the method depends on one of the type parameters being a specific type? For example, let’s say I want a “concatenatedKeys” method that returns all the Dictionary’s keys concatenated into a string with commas between them.

(The actual reason I’m doing this is more complicated, but it has to do with JSON encoding — JSON dictionary keys have to be strings, so I want to be type-safe and make my encoding methods available only for Dictionary<String, ___>.)

—Jens

It appears that you can’t extend a generic class/struct with a requirement that a type parameter inherit from a non-protocol:
  extension Dictionary where Key : String { … }
The above produces the error “Type ‘Key’ constrained to non-protocol type ‘String’”.

You can't do `Key: String` because `String`, as a struct, cannot have any subtypes. `Key: NSString`, for instance, works.

What you really want to do is say `Key == String`, but this isn't supported right now. (My understanding is that this is basically just an implementation shortcut they took.)

One workaround is to define your own protocol and constrain to that:

  protocol _StringType: Hashable {
    // Include the String APIs you need to use here.
  }
  extension String: _StringType {}
  extension Dictionary where Key: _StringType { … }

···

--
Brent Royal-Gordon
Architechies

I thought I replied last week but I don’t see it.

It looks like you can make it work using StringLiteralConvertible. Based on this:

This works:

extension Dictionary where Key: StringLiteralConvertible, Value: Any {
    func testMethod() -> String {
        return "test"
    }
}

var d: [String: Int] = [:]
d["abc"] = 1
print(d.testMethod())

HTH,
Dave Reed

···

On Apr 14, 2016, at 4:58 PM, Jens Alfke via swift-users <swift-users@swift.org> wrote:

It appears that you can’t extend a generic class/struct with a requirement that a type parameter inherit from a non-protocol:
  extension Dictionary where Key : String { … }
The above produces the error “Type ‘Key’ constrained to non-protocol type ‘String’”.

So is there a way I can add a method to a class, where the method depends on one of the type parameters being a specific type? For example, let’s say I want a “concatenatedKeys” method that returns all the Dictionary’s keys concatenated into a string with commas between them.

(The actual reason I’m doing this is more complicated, but it has to do with JSON encoding — JSON dictionary keys have to be strings, so I want to be type-safe and make my encoding methods available only for Dictionary<String, ___>.)

—Jens

You know… if protocols could define which types can implement them, that could be made to be the same thing:
    protocol _String {…} `syntax which restricts instances of _String to be a` String
    extension Dictionary where Key: _String {…} //Same as "where Key == String”, because that’s the only type allowed to implement _String

Is that worth writing proposal over? Or is just saying this:
    protocol _String { func asString() -> String }
    extension String : _String { func asString -> String { return self } }
close enough?

- Dave Sweeris

···

On Apr 14, 2016, at 4:31 PM, Brent Royal-Gordon via swift-users <swift-users@swift.org> wrote:

It appears that you can’t extend a generic class/struct with a requirement that a type parameter inherit from a non-protocol:
  extension Dictionary where Key : String { … }
The above produces the error “Type ‘Key’ constrained to non-protocol type ‘String’”.

You can't do `Key: String` because `String`, as a struct, cannot have any subtypes. `Key: NSString`, for instance, works.

What you really want to do is say `Key == String`, but this isn't supported right now. (My understanding is that this is basically just an implementation shortcut they took.)

One workaround is to define your own protocol and constrain to that:

  protocol _StringType: Hashable {
    // Include the String APIs you need to use here.
  }
  extension String: _StringType {}
  extension Dictionary where Key: _StringType { … }

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

You'll be delighted to know, if you didn't already, that as of Swift 5.1(or earlier), you can do Element == String as you want. Here's an example:

extension Array where Element == String {
    /// Filters array, returning items that are prefixed by any item in the query
    func filter(withAnyPrefix prefixes: [String]) -> Array<Element> {
        return self.filter { (e: String) -> Bool in
            e.hasAnyPrefix(prefixes)
        }
    }
}
2 Likes