john doe
john doe

Reputation: 9660

Returning the Concrete Type Based on Protocol in Swift

I have following code:

protocol Vehicle {
    func start()
}

class Car: Vehicle {
    func start() {
        print("Start car")
    }
}

class MotorCycle: Vehicle {
    func start() {
        print("Start MotorCycle")
    }
}

let vehicles: [Vehicle] = [Car(), MotorCycle()]

func get<T: Vehicle>() -> some Vehicle {
    let result = vehicles.first {
        $0 === T.self
    }
    return result!
}

// so I should be able to do this! 
let car = get<Car>().start() 

Inside the get function I want to go iterate through vehicles and return the concrete type of the Vehicle, which is either Car or MotorCycle. Finally, I want to call start on the returned type. How can I achieve it?

enter image description here

Upvotes: 1

Views: 953

Answers (2)

wzso
wzso

Reputation: 3885

You can not compare an instance value with a type like this: $0 === T.self. You can use $0 is T instead.

And, when calling a generic function, you cannot explicitly specialize a generic function. The type has to be inferred, like @Sweeper said.

Upvotes: 0

Sweeper
Sweeper

Reputation: 271420

This is how get should be written:

func get<T: Vehicle>(_ type: T.Type) -> T? {
    vehicles.first(where: { $0 is T }) as? T
}
  • There might not be any Ts in vehicles, so we should return an optional T.
  • For the implementation, you should use is to check the type in the first(where:) closure. Then, cast to T.
  • You can't directly pass type arguments like <Car> to a function in Swift. Type arguments must always be inferred. So I used a formal parameter type to help Swift infer what T is.

Caller:

get(Car.self)?.start()

Upvotes: 3

Related Questions