Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Generics & Upcasting

I've got a quick question regarding generics in Swift. The problem is I'm trying to store a variable that takes a generic as a parameter, but am unable to cast it up to the type it is restricted by. It's best explained in a short example:

class Foo { }

class Thing<T: Foo> {
    func produceBar() -> Bar {
        return Bar(aThing: self as! Thing<Foo>)
    }
}

class Bar {
    var thing: Thing<Foo>

    init(var aThing: Thing<Foo>) {
        self.thing = aThing
    }
}

The code above produces the error: "Cast from Thing<T> to unrelated type Thing<Foo> always fails"

Shouldn't it never fail, since T is restricted to being a subclass of Foo? I must be misunderstanding the way generics work in Swift, any guidance or help would be much appreciated!

like image 463
Chad Avatar asked May 27 '15 15:05

Chad


People also ask

What are generics Swift?

Generics in Swift allows you to write generic and reusable code, avoiding duplication. A generic type or function creates constraints for the current scope, requiring input values to conform to these requirements.

Why do we need generics in Swift?

Swift Generics allows us to create a single function and class (or any other types) that can be used with different data types. This helps us to reuse our code.

What is difference between any and generics in Swift?

In Summary For now, know that some or generics should be preferred over any if it makes sense. The any keyword should only be used when you really want to use that existential or box type where you'll need to peek into the box at runtime to see what's inside so you can call methods and access properties on it.


1 Answers

Swift generics are not covariant. That is to say, exactly what the error says: you can't automatically say a Basket<Apple> is a kind of Basket<Fruit> even if Apple is a kind of Fruit. There is good reason for this.

Consider the following code:

class Fruit {}
class Apple: Fruit {}
class Orange: Fruit {}

class Basket<T: Fruit> {
    private var items: [T]
    func add(item: T) {
        items.append(item)
    }
    init() {}
}

func addItem<T: Fruit>(var basket: Basket<T>, item: T) {
    basket.add(item)
}

let basket:Basket<Apple> = Basket()

addItem(basket as Basket<Fruit>, Orange())

This would be legal code if Basket<Apple> were considered a Basket<Fruit>, and I'd be allowed to add an orange to a basket of apples.

like image 122
Rob Napier Avatar answered Sep 18 '22 15:09

Rob Napier