some vs any Swift 5.7 — A Practical Example
Of course yes, indeed there are many excellent articles explaining the opaque type and protocol type. But my objective here is to demonstrate the difference with a small practical example so that you can grasp the crux in just a few seconds.
//Blue print of car
protocol Car {
associatedtype Fuel //Fuel type based on the car the conforms it
var engineCC: Int? { get set }
func calculateEmissionTax() -> CGFloat //To calculate taxes based on Fuel used
}
//There are two major types of fuel in the market
//1. Commercial car fuel,
//2. Racing car fuel
enum CommercialFuelType {
case diesel
case petrol
}
enum RacingFuelType {
case Octatane102
case Octatane95
}
There are three segments of automobiles conforming to Car protocol namely FamilyCar, SUV, Sports car. Car protocol has an associated type Fuel.
As you can see that FamilyCar and SUV both use CommercialFuelType.
struct FamilyCar: Car {
typealias Fuel = CommercialFuelType
var fuelType: CommercialFuelType?
let noOfDoors = 4
func calculateEmissionTax() -> CGFloat {
switch fuelType {
case .diesel:
return 15
case .petrol:
return 12
case .none:
return 22
}
}
var engineCC: Int?
}
struct SUV: Car {
var fuelType: CommercialFuelType?
typealias Fuel = CommercialFuelType
let noOfDoors = 5
func calculateEmissionTax() -> CGFloat {
switch fuelType {
case .diesel:
return 25
case .petrol:
return 22
case .none:
return 22
}
}
var engineCC: Int?
}
Sports car uses Racing fuel type
struct SportsCar: Car {
var fuelType: RacingFuelType?
let noOfDoors = 2
typealias Fuel = RacingFuelType
func calculateEmissionTax() -> CGFloat {
switch fuelType {
case .Octatane95:
return 25
case .Octatane102:
return 22
case .none:
return 22
}
}
var engineCC: Int?
}
Alright, now you have got some money and wanted to buy a car. You have two dealers near your home. Dealer A and Dealer B.
Dealer A: Dealer A is a multi-segment car dealer where all the segments are available. So basically you can buy ‘any’ car that conforms to the Car Protocol. The buyCarFromMultiSegmentDealer function returns a protocol type (‘any’ from swift 5.6) meaning it could be of any type that conforms to the Car Protocol, that's why in this case dealer A is able to return SUV or Family or Sports cars. The compiler in this case doesn’t know which type is returned until run time. i.e where it is SUV or Family car or Sports car.
// Dealer A is a multi-segment car dealer where all three segments are available.
// So basically you can buy 'any' car that conforms to the Car protocol
func buyCarFromMultiSegmentDealer(budget: Int) -> any Car {
switch budget {
case 5000...10000:
return FamilyCar(fuelType: .petrol, engineCC: 1300)
case 10000...15000:
return SUV(fuelType: .diesel, engineCC: 1650)
case 15000...20000:
return SportsCar(fuelType: .Octatane102, engineCC: 2200)
default:
return FamilyCar(fuelType: .petrol, engineCC: 1300)
}
}
Dealer B: Dealer B is Sports car specific dealer. You can buy only sports type cars from B. The buyFromSportsCarDealer function returns an opaque type (‘some’)where it returns only one specific concrete type. The compiler in this case knows which type is returned during compile time itself. i.e only Sports cars.
//Dealer B is Sports car specific dealer,
//You can buy only sports type cars from B.
func buyFromSportsCarDealer(minumPrice: Int) -> some Car {
switch minumPrice {
case 5000...10000:
return SportsCar(fuelType: .Octatane95, engineCC: 1500)
case 10000...15000:
return SportsCar(fuelType: .Octatane102, engineCC: 1650)
case 15000...20000:
return SportsCar(fuelType: .Octatane102, engineCC: 2200)
default:
return SportsCar(fuelType: .Octatane95, engineCC: 1500)
}
}
If Dealer A’s return type returns an opaque type (‘some’) we get the below error because the return type don't match. It can return FamilyCar or SUV or Sports car based on the budget
Conclusion: Returning an opaque type (‘some’) looks very similar to using a protocol type (‘any’) as the return type of a function, but these two kinds of return types differ in whether they preserve type identity. An opaque type refers to one specific type, although the caller of the function isn’t able to see which type; a protocol type can refer to any type that simply conforms to the protocol.
Opaque types (‘some’) preserve type identity — the compiler has access to the type information during compile time and can statically allocated memory during compile time, whilst protocol type (‘any’) doesn’t preserve type identity at all and the compiler has to dispatch memory dynamically on run time.
So for compiler performance, Opaque types are better than protocol types but protocol types give you more flexibility about abstracting or hiding the underlying types of the values they store.