I have a following method:
private func returnNilIfEmpty<T: Collection>(_ collection: T?) -> T? {
guard let collection = collection else { return nil }
return collection.isEmpty ? nil : collection
}
I'd like to extend the Collection
API to use a computed variable to extract the non-empty value like this:
extension Collection {
var nonEmptyValue: Collection? {
returnNilIfEmpty(self)
}
}
However, I'm getting the error:
Protocol 'Collection' can only be used as a generic constraint because it has Self or associated type requirements
Which is clear, as the outer Collection
can be any collection (e.g. an [Int]
) while the collection inside the returnNilIfEmpty
can be e.g. a String
.
The question is how can I enforce the rule that the Collection
returned via nonEmptyValue
and the one returned via returnNilIfEmpty
of the same type, so that I can get rid of this compiler error?
CodePudding user response:
It is appropriate to use Self
here:
extension Collection {
var nonEmptyValue: Self? {
returnNilIfEmpty(self)
}
}
If e.g. you did Set([1,2,3]).nonEmptyValue
, you'd expect to get a Set<Int>?
rather than a general Collection?
(you don't even know what kind of elements this type contains or anything like that!), wouldn't you?
CodePudding user response:
Using Self
solves the issue, since it points to the concrete type of the collection:
import Foundation
extension Collection {
var nonEmptyValue: Self? {
returnNilIfEmpty(self)
}
}
func returnNilIfEmpty<T: Collection>(_ collection: T?) -> T? {
guard let collection = collection else { return nil } // nil -> return nil
return collection.isEmpty ? nil : collection // return nil if empty
}
Simplified even further:
import Foundation
extension Collection {
///
var nonEmptyValue: Self? {
self.isEmpty ? nil : self
}
}