为什么 Swift 不能正确推断出这个返回类型?

问题描述 投票:0回答:1

我正在尝试做一些我认为应该非常简单的事情,但是我在 Swift 的类型推断方面遇到了麻烦。我真的不明白为什么会掉在这里。

我有一个类型

Cocktail
,它还有其他属性,但这里唯一重要的是
name
:

struct Cocktail {
    // ... other stuff
    let name: String
}

然后我有两个协议:

protocol ScrollIndexable {
    var scrollIndexTitle: String { get }
}

protocol ScrollIndexProviding {
    var scrollIndices: [any ScrollIndexable] { get }
}

以及

String
ScrollIndexable
的简单一致性:

extension String: ScrollIndexable {
    var scrollIndexTitle: String { self }
}

我想这样做,以便我可以使用一组

Cocktail
作为
ScrollIndexProviding
:

extension Array: ScrollIndexProviding where Element == Cocktail {
    var scrollIndices: [any ScrollIndexable] {
        let firstCharacters = reduce(into: Set<String>()) { partialResult, cocktail in
            guard let firstCharacter = cocktail.name.first else {
                return
            }
            partialResult.insert(String(firstCharacter))
        }
        // The return line here has two errors:
        // Cannot convert return expression of type 'Array<Cocktail>' to return type '[any ScrollIndexable]'
        // No exact matches in call to initializer
        return Array(firstCharacters)
    }
}

此扩展无法构建,有两个错误:

  1. 无法将“Array”类型的返回表达式转换为“[any ScrollIndexable]”返回类型
  2. 调用初始化程序时没有完全匹配

第二个错误对我来说似乎是噪音,因为

Set
符合
Sequence
,所以我应该能够使用该 init 方法

第一个错误让我感到困惑,因为

firstCharacters
数组的类型是
Set<String>
,所以错误消息似乎没有任何意义。我对这里的
any
关键字有什么误解吗?发生什么事了?

swift type-inference
1个回答
2
投票

问题是您位于

Array
的扩展中,其中
Element
Cocktail
,因此当您尝试创建数组而不指定元素类型时,编译器会假设您的意思是元素类型为
Cocktail

extension Array where Element == Cocktail {
    func someMethod() {

        // This array is of type `Array<Cocktail>` since the compiler 
        // assumes the array's element type should be the same as 
        // Self's element type, which (from the extension) is `Cocktail`.
        let array = Array()
    }
}

因此,要解决此问题,只需显式告诉编译器数组的元素类型是 String,如下所示:

extension Array: ScrollIndexProviding where Element == Cocktail {
    var scrollIndices: [any ScrollIndexable] {
        let firstCharacters = reduce(into: Set<String>()) { partialResult, cocktail in
            guard let firstCharacter = cocktail.name.first else {
                return
            }
            partialResult.insert(String(firstCharacter))
        }
        
        return Array<String>(firstCharacters)
        //          ^^^^^^^^ add this
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.