为什么 [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 时,它们可以由数组隐式完成.
考虑以下几点:
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 时,它们可以由数组隐式完成.