I'm studying generics and "some" keyword in Swift and for testing I have this situation:
protocol Vehicle {
var name: String { get }
}
struct Car: Vehicle {
let name = "car"
}
struct Bus: Vehicle {
let name = "bus"
}
Then I tried to create some functions for testing various scenario:
func wash<T : Vehicle>(_ vehicle: T, x : Int) -> T{
if(x < 10){
return Car() as! T
}
return Bus() as! T
}
This function is ok and I know why.
If I change it using "some":
func wash<T : Vehicle>(_ vehicle: T, x : Int) -> some Vehicle{
if(x < 10){
return Car()
}
return Bus()
}
I get this error:
Function declares an opaque return type 'some Vehicle', but the return statements in its body do not have matching underlying types
My question is: both functions I wrote that I want to get Vehicle object, why the second is wrong? (I thought maybe the "some" keyword wants to know the object type to compile time and because there is "if" in the body, it can determinate only runtime, but I don't know).
For resolve the second function problem I tried to use "any", but I don't know because I don't get the error:
func wash<T : Vehicle>(_ vehicle: T, x : Int) -> any Vehicle{
if(y < 10){
return Car()
}
return Bus()
}
Can you explain this difference?
CodePudding user response:
I think the thing you might consider is to change your generic func into a usable one for your case. Why not use your protocol in the first place ?
I mean something like this kind :
func wash(_ vehicle: Vehicle, x : Int) -> Vehicle {
var vehicleRes: Vehicle?
if(x < 10){
vehicleRes = Car()
} else {
vehicleRes = Bus()
}
return vehicleRes!
}
then about your question about some
and any
keywords, your example seems to refer to this article : https://swiftsenpai.com/swift/understanding-some-and-any/
you should read again carefully because all the explanations are there.
CodePudding user response:
func wash<T : Vehicle>(_ vehicle: T, x : Int) -> T{
if(x < 10){
return Car() as! T
}
return Bus() as! T
}
This function is absolutely not ok, and will crash in general. This function says "the caller may request any specific Vehicle type, and this function will return it." That's not what happens at all. It only compiles because you promise (using as!
) that it will work out at runtime, and if it doesn't, then you've asked for the program to crash.
wash(Car(), x: 100) // CRASH
Your second function is the correct one, though today it would be better written:
func wash(_ vehicle: some Vehicle, x: Int) -> any Vehicle { ... }
This says that the caller may pass a specific ("some") Vehicle (i.e. Car, Bus, or something else), and that this function will return a general ("any") Vehicle existential. An "existential" is a wrapper around a specific (concrete) Vehicle that only exposes the interface of the protocol. So the various links in the comments and other answers for much more on the difference between some
and any
.