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

时间:2016-05-29 21:16:33

标签: swift swift-protocols

我有以下代码:

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({ $0.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({ $0.stringTwo == "test string three" })

我不明白为什么最后一行不起作用。任何人都可以填补我吗?我没有看到它与迭代数组并手动添加每个元素有什么不同。

1 个答案:

答案 0 :(得分:3)

这是由于Swift数组在隐式转换它们的类型时受到限制,这是Swift中泛型不变性的结果,并且讨论得更多in this Q&A

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

使用函数式编程解决此问题的最简单方法通常是使用map以允许每个元素上传到TestProtocol(Swift可以从显式类型注释中推断出这一点) :

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

然而,在你的情况下,这是低效的,因为你不得不两次迭代你的数组,顺便提一下你的for循环正在做什么(过滤然后循环元素)。

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

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

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

nil

let protocolArray : [TestProtocol] = testStructOnes.flatMap { $0.stringTwo == "test string three" ? $0 : nil } 非常相似,此处的显式类型注释允许map将每个元素隐式转换为flatMap