-
-
Save LeeKahSeng/a347da1778c71f55537eda991bd56c64 to your computer and use it in GitHub Desktop.
protocol Vehicle { | |
associatedtype FuelType: Fuel | |
var name: String { get } | |
func startEngin() | |
func fillGasTank(with fuel: FuelType) | |
} | |
struct Car: Vehicle { | |
let name: String | |
func startEngin() { | |
print("\(name) enjin started!") | |
} | |
func fillGasTank(with fuel: Gasoline) { | |
print("Fill \(name) with \(fuel.name).") | |
} | |
} | |
struct Bus: Vehicle { | |
let name: String | |
func startEngin() { | |
print("\(name) enjin started!") | |
} | |
func fillGasTank(with fuel: Diesel) { | |
print("Fill \(name) with \(fuel.name).") | |
} | |
} | |
protocol Fuel { | |
associatedtype FuelType where FuelType == Self | |
static func purchase() -> FuelType | |
} | |
struct Gasoline: Fuel { | |
let name = "gasoline" | |
static func purchase() -> Gasoline { | |
print("Purchase gasoline from gas station.") | |
return Gasoline() | |
} | |
} | |
struct Diesel: Fuel { | |
let name = "diesel" | |
static func purchase() -> Diesel { | |
print("Purchase diesel from gas station.") | |
return Diesel() | |
} | |
} | |
// MARK: - Functions | |
func startAllEngin(for vehicles: [any Vehicle]) { | |
for vehicle in vehicles { | |
vehicle.startEngin() | |
} | |
} | |
func fillAllGasTank(for vehicles: [any Vehicle]) { | |
for vehicle in vehicles { | |
fillGasTank(for: vehicle) | |
} | |
} | |
func fillGasTank(for vehicle: some Vehicle) { | |
let fuel = type(of: vehicle).FuelType.purchase() | |
vehicle.fillGasTank(with: fuel) | |
} | |
// MARK: - Execution | |
let vehicles: [any Vehicle] = [ | |
Car(name: "Car_1"), | |
Car(name: "Car_2"), | |
Bus(name: "Bus_1"), | |
Car(name: "Car_3"), | |
] | |
startAllEngin(for: vehicles) | |
fillAllGasTank(for: vehicles) |
Hello, you should review your code:
expression failed to parse: error: PROTOCOL.playground:74:9: error: type 'any Vehicle' cannot conform to 'Vehicle' fillGasTank(for: vehicle) ^ PROTOCOL.playground:74:9: note: only concrete types such as structs, enums and classes can conform to protocols fillGasTank(for: vehicle) ^ PROTOCOL.playground:78:6: note: required by global function 'fillGasTank(for:)' where 'some Vehicle' = 'any Vehicle' func fillGasTank(for vehicle: some Vehicle) { ^
Hi, I suspect the errors that you get are due to the wrong Swift version. Make sure to run the sample code using Swift 5.7 on Xcode 14 (currently in beta).
The sample code was originally written using beta 2 and I just tested it using beta 4 playground, everything seems to work fine. Beta 3 seems to have quite some problems, maybe you can try updating to beta 4 and test it again.
I confirm that with beta 4, it works!
Came across your blog a short while ago and I like the style! Great article, easy to understand with examples. Quite new to generics (and maybe Swift) so thanks a lot for sharing! I wanted something like this last year but couldn't figure out how, and I read online about type erasure to make it work (AnyMyBaseClassObject). Do you have an article on type erasure?
As for this, is it possible to do something like as some Vehicle
or some kind of explicit cast without creating a new function? And what about changing any
to some
for func fillAllGasTank(for vehicles: [any Vehicle])
, will that work?
As of now (Swift 5.7), there is only 1 way to convert opaque type to existential type and vice versa, which is by using a function parameter. Thus using as
will not work. Furthermore, in order for the conversion to work, the function parameter must be of type any
or some
, using [any]
or [some]
will not work.
Hello, you should review your code: