为什么过滤器无法将结构数组转换为协议类型数组?

Why is filter unable to convert an array of structs to an array of a protocol type?

我有以下代码:

protocol TestProtocol {
    var string: String {get}
}

struct TestStructOne : TestProtocol {
    let string = "test string"
    var stringTwo = "test string three"
}

struct TestStructTwo : TestProtocol {
    let string = "test string two"
}

var testStructOnes = [TestStructOne(), TestStructOne(), TestStructOne()]

// works
var protocolArrayOne: [TestProtocol] = [TestProtocol]()
for testStruct in testStructOnes.filter({ [=10=].stringTwo == "test string three" }) {
    protocolArrayOne.append(testStruct)
}

// does not work, cannot copnvert value of type '[TestStructOne]' to specified type '[TestProtocol]'
var protocolArrayTwo: [TestProtocol] = testStructOnes.filter({ [=10=].stringTwo == "test string three" })

我不明白为什么最后一行不起作用。有人可以帮我填吗?我看不出它与遍历数组并手动添加每个元素有何不同。

这是由于 Swift 数组在隐式转换其类型时的限制,这是 Swift 中泛型不变性的结果,并进行了更多讨论 .

解决方案的关键是你不能将数组直接从 [TestStructOne] 转换为 [TestProtocol] – 而是你必须分别转换每个元素,这就是为什么你使用 for 循环的方法并手动将元素附加到新数组。

用函数式编程解决这个问题的最简单方法通常是使用 map 以允许每个元素向上转换为 TestProtocol(Swift 可以推断出这个来自显式类型注释):

let protocolArrayTwo: [TestProtocol] = testStructOnes.filter {
    [=10=].stringTwo == "test string three"
}.map{[=10=]}

但是,在您的情况下,这是低效的,因为您必须遍历数组两次,顺便说一下,这也是您的 for 循环正在做的事情(过滤然后遍历元素)。

相反,您可以使用具有给定 where 条件的 for 循环,以便通过在单次迭代中将它们附加到新数组来过滤出您想要的元素:

var protocolArray = [TestProtocol]()
for testStruct in testStructOnes where testStruct.stringTwo == "test string three"  {
    protocolArray.append(testStruct)
}

或者如果你喜欢函数式编程,你可以使用 flatMap 来过滤掉你不想要的元素(利用 flatMap 过滤掉 nil) 并将它们转换为您的协议类型:

let protocolArray : [TestProtocol] = testStructOnes.flatMap {
    [=12=].stringTwo == "test string three" ? [=12=] : nil
}

map 非常相似,此处的显式类型注释将允许 flatMap 将每个元素单独隐式转换为您的 TestProtocol