Casting Bug Swift


(Anton Zhilin) #1

IMO, the only buggy thing here is that compiler does not complain about an
obvious (for humans) memory corruption.

Have you heard about "fruits-apples-oranges" problem with covariance? I
first learned it from C++, but it holds in pretty much all languages.
Consider the following type hierarchy:

class Fruit {
    func tell() { print("I'm a Fruit!") }
}
class Apple : Fruit {
    override func tell() { print("I'm an Apple!") }
}
class Orange : Fruit {
    override func tell() { print("I'm an Orange!") }
}

Now imagine that covariance in generics was allowed. I will also use
generic notation for arrays here.

var apples: Array<Apple> = []
apples.append(Apple())

var fruits: Array<Fruit> = apples // can we allow this?

fruits.append(Orange())
apples[0].tell() //=> I'm an Orange!

We obviously don't want this to happen.
So we decide to automatically create an empty Array<Fruit> and call
append() with all casted elements of the initial array whenever arrays are
casted:

var fruits: Array<Fruit> = copyThatArray(apples) as Array<Fruit>

Now people will come and ask, why can't I do the same with Set<Fruit>? With
Box<Fruit> from that library? But everything works on Array<Fruit>! Why??

And so I end my story with conclusion that we should not allow covariant
generics in any form.


(Joe Groff) #2

This is a problem for reference-semantics arrays, but Swift's arrays have value semantics. `var fruits: Array<Fruit> = apples` creates a copy of the array, so adding Oranges to `fruits` doesn't affect `apples`.

-Joe

···

On Feb 25, 2016, at 1:01 PM, Антон Жилин via swift-evolution <swift-evolution@swift.org> wrote:

Now imagine that covariance in generics was allowed. I will also use generic notation for arrays here.

var apples: Array<Apple> = []
apples.append(Apple())

var fruits: Array<Fruit> = apples // can we allow this?

fruits.append(Orange())
apples[0].tell() //=> I'm an Orange!

We obviously don't want this to happen.
So we decide to automatically create an empty Array<Fruit> and call append() with all casted elements of the initial array whenever arrays are casted:

var fruits: Array<Fruit> = copyThatArray(apples) as Array<Fruit>

Now people will come and ask, why can't I do the same with Set<Fruit>? With Box<Fruit> from that library? But everything works on Array<Fruit>! Why??

And so I end my story with conclusion that we should not allow covariant generics in any form.