Kevin Wessels
Kevin Wessels

Reputation: 85

What is my type not inferable in this case?

I have a protocol:

protocol Model {}

And structs using this protocol:

struct Foo: Model {}

Then I have a different protocol with a generic:

protocol Controller {
    func fun<T: Model>() -> Observable<T>
}

The controller's implementation of the method can infer the type:

class Bar: Controller {
    func fun<Foo>() -> Observable<Foo> {
        // return an observable
    }
}

What I thought I was doing here was have two 'base' protocols, one for models and one for controllers. The base controller dictates a method to implement using a generic of the base model group, the implementation can then specify which model to use. After writing all that it compiles fine and everything seems in order, but when I make an instance of Bar and try to call fun() on it I get an error saying Generic parameter 'Foo' can not be inferred.
Here's a minimum complete example to reproduce with:

protocol Model {}

struct Foo: Model {}

protocol Controller {
    func fun<T: Model>() -> Observable<T>
}

class Bar: Controller {
    func fun<Foo>() -> Observable<Foo> {
        return Observable.create { observer in

            let cancel = Disposables.create {
                // clean up
            }

            return cancel
        }
    }
}

Calling Bar().fun() will now give the error: Generic parameter 'Foo' can not be inferred

There are two things I don't understand here. The first is how can it know that I call the implementation that specified Foo while at the same time not being able to infer the type? Foo is not a generic, it's the actual type of the Foo struct. The second thing I don't understand and the actual question here, is if I call a method on an instance that implemented and specified a generic method, why can it not infer the type?

Upvotes: 1

Views: 59

Answers (3)

Sweeper
Sweeper

Reputation: 270790

I think your misunderstanding is mistaking the Foo in this method as the Foo struct:

func fun<Foo>() -> Observable<Foo> {
    // return an observable
}

It's not! Foo in the context of the above method is just another generic type parameter!


What you are trying to achieve here...

The base controller dictates a method to implement using a generic of the base model group, the implementation can then specify which model to use.

...should be done with associated types:

protocol Model {}

struct Foo: Model {}

protocol Controller {
    associatedtype ModelType: Model
    func fun() -> Observable<ModelType>
}

class Bar: Controller {
    typealias ModelType = Foo
    func fun() -> Observable<Foo> {
        return Observable.create { observer in

            let cancel = Disposables.create {
                // clean up
            }

            return cancel
        }
    }
}

Upvotes: 1

Bojan Dimovski
Bojan Dimovski

Reputation: 1216

First, you'll need to fix the implementation of the fun method:

class Bar: Controller {
    func fun<T: Model>() -> Observable<T> {
        return Observable<T>.create { observer in

            let cancel = Disposables.create {
                // clean up
            }

            return cancel
        }
    }
}

Then, you'll need to infer the generic Foo parameter within the caller like:

let observableFoo: Observable<Foo> = Bar().fun()

Upvotes: 0

iDevid
iDevid

Reputation: 517

    func fun<Foo>(_ type: Foo.Type) -> Observable<Foo> {
        return Observable.create { observer in

            let cancel = Disposables.create {
                // clean up
            }

            return cancel
        }
    }

Then you can call it with:

Bar.fun(YourModel.self)

Upvotes: 0

Related Questions