为什么 [SomeStruct] 不能转换为 [Any]?

Why isn't [SomeStruct] convertible to [Any]?

考虑以下几点:

struct SomeStruct {}

var foo: Any!
let bar: SomeStruct = SomeStruct()

foo = bar // Compiles as expected

var fooArray: [Any] = []
let barArray: [SomeStruct] = []

fooArray = barArray // Does not compile; Cannot assign value of type '[SomeStruct]' to type '[Any]'

我一直试图找到这背后的逻辑,但没有运气。 值得一提的是,如果将结构更改为 class,它会完美运行。

总是可以添加一个解决方法并映射 fooArray 的每个对象并将它们转换为 Any 类型,但这不是这里的问题。我正在寻找关于为什么会这样的解释。

有人可以解释一下吗?

This SO question led me to this problem.

Swift 无法在包含值类型和引用类型的数组之间自动转换。 只需将数组映射到您需要的类型即可:

fooArray = barArray.map({ $0 }) // 编译

Swift 3 次更新

从 Swift 3(特别是 Xcode 8 beta 6 附带的版本)开始,collection 类型现在可以从 value-typed 元素进行幕后转换collections 到 abstract-typed 元素 collections.

这意味着现在将编译以下内容:

protocol SomeProtocol {}
struct Foo : SomeProtocol {}

let arrayOfFoo : [Foo] = []

let arrayOfSomeProtocol : [SomeProtocol] = arrayOfFoo
let arrayOfAny : [Any] = arrayOfFoo

前 Swift 3

这一切都始于这样一个事实,即 Swift 中的泛型是不变的——不是协变的。请记住 [Type] 只是 Array<Type> 的语法糖,您可以抽象出数组和 Any 以希望更好地看到问题。

protocol Foo {}
struct Bar : Foo {}

struct Container<T> {}

var f = Container<Foo>()
var b = Container<Bar>()

f = b // error: cannot assign value of type 'Container<Bar>' to type 'Container<Foo>'

与类类似:

class Foo {}
class Bar : Foo {}

class Container<T> {}

var f = Container<Foo>()
var b = Container<Bar>()

f = b // error: cannot assign value of type 'Container<Bar>' to type 'Container<Foo>'

Swift 中的泛型根本不可能实现这种协变行为(向上转型)。在您的示例中,由于不变性,Array<SomeStruct> 被视为与 Array<Any> 完全无关的类型。

但是,数组有一个例外——它们可以在幕后默默地处理从子类类型到超类类型的转换。但是,当将具有 value-typed 个元素的数组转换为具有 abstract-typed 个元素的数组(例如 [Any])时,它们不会执行相同的操作。

要解决这个问题,您必须执行自己的 element-by-element 转换(因为各个元素是协变的)。实现此目的的常见方法是使用 map(_:):

var fooArray : [Any] = []
let barArray : [SomeStruct] = []

// the 'as Any' isn't technically necessary as Swift can infer it,
// but it shows what's happening here
fooArray = barArray.map {[=13=] as Any} 

这里避免隐式 'under the hood' 转换的一个很好的理由是 Swift 在内存中存储抽象类型的方式。 'Existential Container' 用于将任意大小的值存储在固定的内存块中——这意味着对于无法容纳在该容器中的值,可能会发生昂贵的堆分配(只允许对要存储的内存的引用)在这个容器中)。

因此,由于现在数组在内存中的存储方式发生了重大变化,因此禁止隐式转换是非常合理的。这使得程序员明确表示他们必须转换数组的每个元素——导致内存结构发生这种(可能代价高昂的)变化。

有关 Swift 如何处理抽象类型的更多技术细节,请参阅有关该主题的 this fantastic WWDC talk on the subject. For further reading about type variance in Swift, see this great blog post

最后,确保查看 @dfri's comments below 关于数组可以隐式转换元素类型的其他情况——即当元素可桥接到 Objective-C 时,它们可以由数组隐式完成.