简体   繁体   中英

Swift 3 generic function ambiguous return

I need a single function to resolve different dependencies in a class. But there is a compilation error appears. Is it possible to create that generic function or there are some compiler constraints in Swift?

import Foundation

protocol Client: class {
    var description: String { get }
}
final class ImportantPerson : Client {
    var description: String {
        return "Important person"
    }
}

protocol Order: class {
    var description: String { get }
}
final class LastOrder : Order {
    var description: String {
        return "Last order"
    }
}

final class A {

    fileprivate func resolveDependency<T>() -> T {
        return resolve() as T
    }

    private func resolve() -> Client {
        return ImportantPerson()
    }
    private func resolve() -> Order {
        return LastOrder()
    }

}

let a = A()
let client: Client = a.resolveDependency()
let order: Order = a.resolveDependency()

print("Client: \(client.description)")
print("Order: \(order.description)")

EDIT : This question is not about if Swift allows to create two functions that differs only by return type. I know it's possible. I think there are some artificial constraints in the compiler but not in the fundamental logic that should allow to infer needed type from a context.

Let's put yourself into the compiler's shoes. Imagine that this was not causing an error and you had one signature with different outputs.

Whenever you call resolveDependency<T>() -> T , the compiler will return you a type T which is an instance conforming to a protocol in your case.

In your code you call this method with different instances conforming to the same protocol. At that stage the compiler has no idea about this. All it knows is that you have passed an instance of T and it needs to give you a result in shape of T

There is no problem until this point. As soon as you execute

return resolve() as! T

The compiler will be confused. I have a T but I don't know which resolve() I will call... All I know is that I have a T . How would I know if this is an Order or a Client ?

In order to prevent such confusions we have compiler-time errors. At least this is the case for Swift. (I don't know how this works in other languages)

You need to define different methods with different signatures and cast your type accordingly to get a similar result

fileprivate func resolveDependency<T>() -> T {
  // check if this is an Order
  resolveForOrder()

  // check if this is a Client
  resolveForClient()
}

private func resolveForOrder() -> Order {
  return LastOrder()
}

private func resolveForClient() -> Client {
  return ImportantPerson()
}

This is like trying to fix a space shuttle engine with a car mechanic. Yes, they both have an engine, they both run on fuel but the mechanic only knows how to fix your car's engine he is not a rocket scientist(!)

This code works fine:

import Foundation

protocol Client: class {
    var description: String { get }
}
final class ImportantPerson : Client {
    var description: String {
        return "Important person"
    }
}

protocol Order: class {
    var description: String { get }
}
final class LastOrder : Order {
    var description: String {
        return "Last order"
    }
}

final class A {

    fileprivate func resolveDependency<T>() -> T {
        if T.self == Client.self {
            return resolve() as Client as! T
        } else {
            return resolve() as Order as! T
        }
    }

    private func resolve() -> Client {
        return ImportantPerson()
    }
    private func resolve() -> Order {
        return LastOrder()
    }

}

let a = A()
let client: Client = a.resolveDependency()
let order: Order = a.resolveDependency()

print("Client: \(client.description)")
print("Order: \(order.description)")

But I believe that compiler should resolve the if else clause himself, it's not so hard as I suppose. Also there is some bug in the compiler when it tries to match types like that:

    switch T.self {
    case is Client:
        return resolve() as Client as! T
    default:
        return resolve() as Order as! T
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM