How can I not specify generics?

Hello everyone, I am a swift beginner, and here is a problem I have:
example code like this:

struct Cat<T> {
    let food: T
    let eat: (T) -> ()
    let size: Int
}

// all cats eat their own food in order
func feed<T>(cats: Cat<T>...) {
    let orders = cats.indices.sorted(by: { i, j in cats[i].size > cats[j].size })
    for i in orders {
        let cat = cats[i]
        cat.eat(cat.food)
    }
}

But the feed function can only deal with one kind of Cats, actually I want do things like this:

typealias Cats2<A, B> = (Cat<A>, Cat<B>)

func feed<A, B>(cats: Cats2<A, B>) {
    if cats.0.size > cats.1.size {
        cats.0.eat(cats.0.food)
        cats.1.eat(cats.1.food)
    } else {
        cats.1.eat(cats.1.food)
        cats.0.eat(cats.0.food)
    }
}

It's impossible to write down all cases:

typealias Cats3<A, B, C> = (Cat<A>, Cat<B>, Cat<C>)
typealias Cats4<A, B, C, D> = (Cat<A>, Cat<B>, Cat<C>, Cat<D>)

func feed<A, B, C>(cats: Cats3<A, B, C>) {
}

func feed<A, B, C, D>(cats: Cats4<A, B, C, D>) {
}

Is there a way I can do to achieve this: func feed(cats: Cat...) which accept various types of <T> ?

I use protocol and Any solved my problem.

And just ask another syntax question here

If there is something define like this:

struct Box<T> {}
struct Cat<T> {
    let box: Box<T>
}

and another define:

protocol AnyBox {}
protocol AnyCat {
    var box: AnyBox { get }
}

And I want extension Cat<T> to AnyCat

extension Box: AnyBox {}
extension Cat: AnyCat {}

But because same box property name, this can not be done. How can I avoid name conflict if I can't edit these define?

still can not work ...

Variadic Generics are not supported in Swift.

2 Likes

I don't think you can do that in an ideal way yet. I saw there was some talk about "opening existentials" here, but if you're a Swift beginner you probably don't want to read that. It makes me dizzy and I've been using Swift for a while.

If you really need a list of Cats with different type params, one possibility is to create a "type erased" version of a Cat...

struct AnyCat {
    let cat: Any
    let eatFood: () -> Void
    let eat: (Any) -> Void
    let getFood() -> Any
    init<T>(_ cat: Cat<T>) { 
        self.cat = cat
        self.eatFood = { cat.eat(cat.food) }
        self.eat = { anyFood in cat.eat(anyFood as! T) }
        self.getFood = { cat.food }
    }
    var food: Any { getFood() }
}

var cats = [AnyCat(Cat<Int>()), AnyCat(Cat<String>())]
for cat in cats {
    cat.eatFood()
}

Disclaimer: I did not put that in Xcode. There may be typos or mistakes, but I hope it shows the idea. In the init of the AnyWhatever, you can create closures that know about T, cast from Any to T, etc., in order to create properties and methods of the AnyCat as needed.

1 Like

This is called variadic generics, and as @jmjauer says, it's not supported in Swift at this time. So the answer to your question is simply that there is no way you can achieve that.

Thanks. Your example code inspired me. Finally I solved my problem using type erased.

Terms of Service

Privacy Policy

Cookie Policy