通用类的Swift扩展类型约束,其中通用类型是另一种通用类型

时间:2019-11-21 14:43:55

标签: swift generics swift-extensions type-constraints

我正在设法绕过Swift中的泛型类型约束。这是我的出发点:

class Promise<T> {
    func resolve(_ block:@escaping (T) ->Void) {}
    func fulfill(_ result:T) {}
}

一个承诺就是,将来可以实现。当将结果从后台队列传递回主队列时,当它与Swift的Result类型一起使用时,这将非常有用:

let promise = Promise<Result<String, Error>>()
promise.fulfill(.success("Hello"))
promise.fulfill(.failure(NSError()))

现在,我想为所有使用Result添加这些帮助方法的Promise实例添加扩展名:

extension Promise where T == Result<X, Error> {
                                    ⬆︎ Here's the problem ⚡️
    func failure(_ error:Error) {
        fulfill(.failure(error))
    }

    func success(_ result:X) {
        fulfill(.success(result))
    }
}

// Shorter:
let promise = Promise<Result<String, Error>>()
promise.success("Hello")
promise.failure(NSError())

唯一的问题是上述代码无法编译,因为未定义X。我要表达的是这样:

当其通用类型Promise的类型为T时扩展Result<X,Z>,其中X可以为任意值,而Z的类型必须为ErrorResult<*, Error>。这可能吗?

1 个答案:

答案 0 :(得分:1)

您想要的是可能的,只是语法有些冗长。您不能将where约束放在扩展名上。您必须将其放在每种方法上。

extension Promise {

    func failure<U>(_ error: Error) where T == Result<U, Error> {
        fulfill(.failure(error))
    }

    func success<U>(_ result: U) where T == Result<U, Error> {
        fulfill(.success(result))
    }
}

您也可以使用协议来做到这一点,但是对于枚举,我觉得这很笨拙,因为不能将枚举的情况视为符合条件的方法。

protocol ResultType {
    associatedtype Success
    associatedtype Failure: Error
    static func makeFailure(_: Failure) -> Self
    static func makeSuccess(_: Success) -> Self
}

extension Result: ResultType {
    static func makeFailure(_ failure: Failure) -> Result<Success, Failure> { .failure(failure)  }
    static func makeSuccess(_ success: Success) -> Result<Success, Failure> { .success(success) }
}

extension Promise where T: ResultType {
    func failure(_ error: T.Failure) {
        fulfill(T.makeFailure(error))
    }

    func success(_ result: T.Success) {
        fulfill(T.makeSuccess(result))
    }
}