indirect or recursive enums — A Practical Example
A recursive enumeration is an enumeration that has another instance of the enumeration as the associated value for one or more of the enumeration cases. You indicate that an enumeration case is recursive by writing
indirect
before it, which tells the compiler to insert the necessary layer of indirection.
Let’s try to understand with a simple use case. We create a protocol Cars and define the attributes.
//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
}
Let's create three types of Cars conforming to Cars protocol namely FamilyCar, SUV, Sports car.
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?
}
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?
}
However, there is a fourth type of car called the government utility car where the government decides to buy one for its service purpose. The government utility car could be from any segment i.e it could be a FamilyCar, SUV or Sports car.
enum CarType {
case family(FamilyCar)
case suv(SUV)
case sports(SportsCar)
case govtUtility(CarType) //Govt could buy any above type based on the need
}
So here we can see the enum CarType has a car type that references to a CarType itself as an associated value for the govtUtility car.
Indirection (also called dereferencing) is the ability to reference something using a name, reference, or container instead of the value itself. The most common form of indirection is the act of manipulating a value through its memory address.
Since CarTpye has a case that has CarType itself as an associated value for one of the cases, the compiler cannot calculate the size of this enum during compile type, as govtUtility could be either Family car or SUV or Sports car on run time. We indicate that an enumeration case is recursive by writing indirect
before the case, which tells the compiler to insert the necessary layer of indirection such that the compiler stores the data in a memory address (pointer) which enables dynamic memory allocation with optimization.
enum CarType {
case family(FamilyCar)
case suv(SUV)
case sports(SportsCar)
indirect case govtUtility(CarType) //Govt could buy any above type based on the need
}
Note: You can write indirect before the specific case or if all the cases are recursive you can mark the entire enum as indirect.