I'd like to return a UIViewController
conforming to MyProtocol
from a method, so I'm using the method signature:
func myMethod<T where T : UIViewController, T : MyProtocol>() -> T {
First thing I don't understand: if myMethod
returns e.g. a MyViewController
which has to following signature, I have to force cast it:
class MyViewController: UIViewController, MyProtocol
I cannot simply return MyViewController()
but I need to cast it like this: return MyViewController() as! T
- why is this necessary?
And the second thing: how can I use this method somewhere? I cannot simply say
let x = myMethod() as? UIViewController
as I get the error
Generic parameter 'T' could not be inferred
How can I achieve something like this? If I cast it to MyViewController
it works, but I would like to avoid that of course.
EDIT: Example
class MyViewController : UIViewController, MyProtocol {
}
protocol MyProtocol {
}
func myMethod<T>() -> T where T : UIViewController, T : MyProtocol {
return MyViewController() as! T // why is the cast necessary?
}
ok, I do get one part, but why is the cast to T
necessary? MyViewController
is a subclass of UIViewController
and conforms to the protocol, so no cast should be necessary, right?
In a method like this, returning
T
means you have to returnT
. If you returnMyViewController
, the return type should beMyViewController
.T
is a generic type that will take the form of whatever the Swift compiler can infer it to be.So, with your method signature, a simple implementation of the protocol and method could look like this.
So, considering your usage example:
How would the compiler know what the concrete type of
T
is? There is nothing giving it a hint ofMyViewController
. The only thing we know is that whateverT
is, it should beMyViewController
or a subclass of it. And it should conform toMyProtocol
. But this does not provide information about what the type ofT
should be.The only place where the compiler can infer what we want
T
to be is through the return value. All the code between<>
are constraints for whatT
is allowed to be.-> T
is the only place whereT
is seen outside of the constraints. So if we can somehow tell the compiler what we wantmyMethod
to return, we have given it enough information to inferT
.Your typecast works but I agree that it's not very pretty. A much prettier way for the compiler to infer
T
is this.By specifying the type of
vc
, the compiler understands that we wantmyMethod
to return aMyViewController
. So nowT
's type can be inferred and if we returnT
, we actually returnMyViewController
.As some pointed out in the comments, there is no apparent reason for
myMethod
to be generic. The argument for doing so is: (quoting from your comment)Lets call that type
ViewControllerAndMyprotocol
,However
myMethod
signature already constrains the typeViewControllerAndMyprotocol
i.e. caller is bound to receive aUIViewController
and not any of the "different classes which conform to this rules".The flexibility on what concrete types could be
ViewControllerAndMyprotocol
, includingMyViewController
is why there is type ambiguity in the statementlet x = myMethod()
requiring casting:let x = myMethod() as? UIViewController
You can avoid the casting by changing
myMethod
signature as such:The statement
let x = myMethod()
will not require casting and will be of typeViewControllerAndMyprotocol
which is also aUIViewController
.This declaration says: There exists a function called
myMethod
, such thatmyMethod
returns some specificT
whereT
is a subtype ofUIViewController
and alsoMyProtocol
. This does not say what typeT
actually is, and it does not say that there is only one suchmyMethod
. There can be many if there are many type that are both subclasses ofUIViewController
and conform toMyProtocol
. Every one of those types creates a new version ofmyMethod
(really a new solution to the assertionmyMethod
makes, that such a function does exist).This is not the same thing as:
That says: The function
myMethod
returns any subtype ofUIViewController
.There is no way in Swift to express "any type that is a subclass of UIViewController and is a subtype of MyProtocol." You can only discuss a specific type that meets that criterial. Swift can't combine classes and protocols this way; it's just a current limitation of the language, not a deep design issue.
The specific versus any is the issue. There are many functions that satisfy your
myMethod
declaration. EveryT
you can plug in that conforms to the rules would be a candidate. So when you saymyMethod()
, the compiler doesn't know which specificT
you mean.(I was going to expand this answer to provide it in less type-theory, more "how do you do it in code" terms, but donnywals already has an excellent version of that.)
* To your edited question *
T
is a specific type decided by the caller. It is not "any type that conforms" it is "some specific, concrete type that conforms." Consider the case that you called:In this case,
T
isSomeOtherViewController
.MyViewController
is not that type, so what you're doing with theas!
cast is dangerous.