skip to Main Content

I have a question regarding using the some vs any keyword in Swift. As you may see in the below code that the init method in CarVersion1 uses some while CarVersion2 use any. My question is which version is better and why?

protocol Engine { var typeName: String { get } }

final class ElectricEngine: Engine { var typeName: String { "Electric Engine" } }
final class PetrolEngine: Engine { var typeName: String { "Petrol Engine" } }
final class DieselEngine: Engine { var typeName: String { "Diesel Engine" } }

final class CarVersion1 {
    private var engine: any Engine
    init(engine: any Engine) { self.engine = engine }
    func printEngineType() { print(engine.typeName) }
}

final class CarVersion2 {
    private var engine: any Engine
    init(engine: some Engine) { self.engine = engine }
    func printEngineType() { print(engine.typeName) }
}

let car1 = CarVersion1(engine: ElectricEngine())
car1.printEngineType()
let car2 = CarVersion2(engine: ElectricEngine())
car2.printEngineType()

2

Answers


  1. The following are almost identical:

    init(engine: some Engine) { self.engine = engine }
    
    init<T: Engine>(engine: T) { self.engine = engine }
    

    Because some is resolving the underlying concrete type at the compile time.

    any on the other hand, acts very similar but unlike some and generic, it will NOT expose the actual concrete type until the runtime.

    Usually, you should prefer to use some (compile-time) over any (run-time) anywhere you can for the sake of performance, safety and reliability.

    Login or Signup to reply.
  2. In your specific case, it doesn’t particularly matter, since you immediately turn around and store it in an any Engine. The any existential box has to be constructed in either case, and so you don’t get much benefit from using some in the init. (It’s possibly a little worse, but I would expect it generally just doesn’t matter.)

    Where you would get some benefits is if you made Car generic over its Engine:

    final class CarVersion3<E: Engine> {
        private var engine: E
        init(engine: E) { self.engine = engine }
        func printEngineType() { print(engine.typeName) }
    }
    
    let car3 = CarVersion2(engine: ElectricEngine())
    car3.printEngineType()
    

    This way there is no existential box required. More code can be inlined. More dispatch is static rather than dynamic. When reasonable, this is the preferred approach. The only problem is that because CarVersion3 is generic, you cannot create an Array of multiple cars with different engines. And if you need to store this in a property, it may force the containing type to also be be generic. Generics spread. That’s the trade-off.

    I realize in CarVersion3 there is no some. So, to get to your underlying question, some is fairly unusual in an init parameter. You cannot have a stored property of a some type, so there usually isn’t a reason to pass one to init. The generic type is expressed on the container, not on the init.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search