无法在Swift中的数组类型的扩展函数中使用Array初始值设定项

0yycz8jy  于 2023-04-19  发布在  Swift
关注(0)|答案(2)|浏览(213)

使用Swift 5.7.2和Xcode 14.2,我试图编写一个扩展函数到某个类型的数组,即[MyClass]。在函数中,我希望能够使用Array()初始化器将集合转换为数组,但我不能。我得到以下错误:No exact matches in call to initializer
为了模拟这个问题,我用下面的代码创建了一个小操场,在那里我只是尝试扩展[Int]。另外,我意识到这只是扩展数组时的问题,因为当我扩展Int类型时不会出现错误。
我非常好奇为什么会发生这种情况,我希望有人能帮我弄清楚。这很可能有一个合乎逻辑的解释。

扩展[Int](不起作用)

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)
    }
}

扩展Int(工作正常)

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)
    }
}

不是扩展(工作正常)

func foo() {
    let strSet = Set(["a", "b", "c", "a"])
    let strArray = Array(strSet)
    print(strArray)
}
qvtsj1bj

qvtsj1bj1#

查看更详细的错误消息:

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

Swift认为你正在创建一个Array<Int>
如果只指定泛型类型参数,它将按预期工作:

let strArray = Array<String>(strSet)

这是问题SR-1789的一个示例。
通常,当你在泛型类型的类型声明/扩展中时,你可以使用没有泛型参数的类型,类型参数将被推断为你声明的类型参数。就像在这些情况下:

extension [Int] {
    func foo() -> Array { // just writing "Array" works, no need to say Array<Int>
        fatalError()
    }
}

或者,

class Foo<T> {
    func foo() -> Foo { // just writing "Foo" works, no need to say Foo<T>
        fatalError() 
    }
}

但是,这个功能似乎太“咄咄逼人”了,从某种意义上说。

qojgxg4l

qojgxg4l2#

Sweeper的回答描述了你遇到的问题,但这不是一个bug。这只是语言的工作方式。在泛型类型的定义或扩展中,该类型的名称指的是专用版本,而不是泛型版本。插图:

struct Generic<Placeholder> { }

extension Generic<Never> {
  func generic(generic: Generic) -> Self { generic }
}

Array在这方面并不特殊;您应该编辑问题标题。)
虽然.init不适合您的用例,但它通常是推断正确类型的好解决方案:

extension Generic<Never> {
  func generic<T>() -> Generic<T> { .init() }
}

Generic().generic() as Generic<Bool>

编译,而使用非专用化类型的名称将不会:

extension Generic<Never> {
  // Cannot convert return expression of type 'Generic<Placeholder>' to return type 'Generic<T>'
  func generic<T>() -> Generic<T> { Generic() }
}

避免显式指定 * 占位符类型 * 的另一种方法是使用 *type占位符 *:

extension Generic<Never> {
  func generic<T>() -> Generic<T> { Generic<_>() }
}

您可以选择Array<String>[String]Array<_>[_]

let stringSet = ["a", "b", "c", "a"] as Set
let stringArray = [_](stringSet)
print(stringArray)

相关问题