That's really not. In my code, I'm not casting something to Any
, reassigning the whole thing, and then force casting it back to something unrelated. I'm exploiting the fact that the compiler accepts [Any]
as a supertype of [Int]
, but that's not strictly true, because not all operations that I can do on [Any]
can be done on [Int]
. That's why I say that it's unsound.
Consider this code:
class Foo {
func frobulate() {}
}
class Bar {
func frobnicate() {}
}
class Baz: Bar {}
var x = Baz()
var y = x as Bar // No issue
var z = x as Foo // Cast from 'Baz' to unrelated type 'Foo' always fails
The compiler doesn't complain on let y = x as Bar
, and it shouldn't because everything that can be done with a Bar
can also be done with a Baz
, that's the point of subtyping (please note that this is unrelated to the Liskov substitution principle, that's often thrown around: that deals with behavioral [i.e. semantics] subtyping, but that's not the point here, it's really just about being able to operate on the same interface).
Consider this then:
var xs = [1, 2, 3]
var ys = xs as [Any] // No issue
var zs = xs as [String] // Fail
The compiler accepts the line var ys = xs as [Any]
, but it really shouldn't, because there are some things that I can call on a mutable [Any]
that I cannot call on a mutable [Int]
. The runtime, as far as I know, solves the conundrum by dynamically modifying the internals of the array instance: that's what I actually wanted to show with my original code, that something is happening at runtime in the background that produces unexpected results.
This is solved with the example from @anon9791410 that uses the more precise some RangeReplaceableCollection
and any RangeReplaceableCollection
.
It's not the same because, semantically, in the map
case you're creating a new instance of the array, so there's no reason to consider it related to the original instance at the type level.