Using Swift 5.7.2 and Xcode 14.2 I am attempting to write an extension function to an array of a certain type i.e. [MyClass]
. Inside the function I would like to be able to use the Array()
initializer to convert a set to an array, but I am not able to. I get the following error: No exact matches in call to initializer
.
To simulate the problem I created a small playground with the code found below, where I simply try to extend [Int]
. Additionally I realized that this is only a problem when extending an array, as the error does not appear when I extend just the Int
type.
I am super curious about why this happens, and I hope someone can help me figure it out. There is most likely a logical explanation to this.
Extending [Int] (does not work)
extension [Int] {
func foo() {
let strSet = Set(["a", "b", "c", "a"])
let strArray = Array(strSet) // No exact matches in call to initializer
print(strArray)
}
func bar() {
let strSet = Set(["a", "b", "c", "a"])
let strArray = strSet.map {$0}
print(strArray)
}
}
Extending Int (works fine)
extension Int {
func foo() {
let strSet = Set(["a", "b", "c", "a"])
let strArray = Array(strSet) // Works fine
print(strArray)
}
func bar() {
let strSet = Set(["a", "b", "c", "a"])
let strArray = strSet.map {$0}
print(strArray)
}
}
Not an extension (works fine)
func foo() {
let strSet = Set(["a", "b", "c", "a"])
let strArray = Array(strSet)
print(strArray)
}
By looking at the more detailed error message:
Swift.Array:3:23: note: candidate requires that the types 'Int' and 'String' be equivalent (requirement specified as 'Element' == 'S.Element')
@inlinable public init<S>(_ s: S) where Element == S.Element, S : Sequence
^
Swift.RangeReplaceableCollection:3:23: note: candidate requires that the types 'Int' and 'String' be equivalent (requirement specified as 'Self.Element' == 'S.Element')
@inlinable public init<S>(_ elements: S) where S : Sequence, Self.Element == S.Element
It appears that Swift thinks you are trying to create a Array<Int>
.
If you just specify the generic type parameter, it will work as intended:
let strArray = Array<String>(strSet)
This is an instance of the issue SR-1789.
Normally, when you are in a type declaration/extension of a generic type, you are allowed to use that type without generic arguments, and the type arguments will be inferred as the type parameters you declared. Like in these situations:
extension [Int] {
func foo() -> Array { // just writing "Array" works, no need to say Array<Int>
fatalError()
}
}
or,
class Foo<T> {
func foo() -> Foo { // just writing "Foo" works, no need to say Foo<T>
fatalError()
}
}
However, this feature seems to be too "aggressive", in a sense.