泛型约束

时间:2020-01-10 08:23:25

标签: swift generics

有人可以帮助解决方案吗,正如我在底部所描述的那样,任何变体都无法通过(或者不是??),但是也许有人知道非常接近的解决方案?

P.S。请查看代码注释中的描述。

import Foundation

public struct HTTPRequest {
    // ...
}

public struct HTTPResponse {
    // ...
}

public class Router<SuccessResponse, FailureResponse: Swift.Error> {

    internal typealias Encoder = (Result<SuccessResponse, FailureResponse>) -> HTTPResponse
    internal typealias Responder = (HTTPRequest) -> HTTPResponse

    private let encoder: Encoder
    internal private(set) var responders: [String: Responder]

    internal init(encoder: @escaping Encoder) {
        self.encoder = encoder
        self.responders = [:]
    }

    // For me, is not correct! Description further...
    public func on(_ path: String, using closure: @escaping (HTTPRequest) -> Result<SuccessResponse, FailureResponse>) {
        responders[path] = { request in
            let result = closure(request)
            return self.encoder(result)
        }
    }

    // It's correct way, in usege, in this variant, you can't use different subtypes of SuccessResponse and FailureResponse, only one concrate type!
    // BUT I can't set constraint on SuccessResponse and FailureResponse as this should be a Protocol and in result we have error `... constrained to non-protocol, non-class type ...`
    public func on<S: SuccessResponse, F: FailureResponse>(_ path: String, using closure: @escaping (HTTPRequest) -> Result<S, F>) { // Type 'S, ''F' constrained to non-protocol, non-class type 'FailureResponse'
        responders[path] = { request in
            let result = closure(request)
            return self.encoder(result)
        }
    }

}

使用示例,您不能使用任何ApiSuccess或ApiFailure,只能使用受其他泛型约束的具体类型:

protocol ApiSuccess {
    // ...
}
protocol ApiFailure {
    // ...
}

enum Endpoint1Success: ApiSuccess {
    case ok
    case empty
}
enum Endpoint1Failure: ApiFailure {
    case not
    case internalError
}


let router = Router<ApiSuccess, ApiFailure> { result -> HTTPResponse in
    switch result {
    case .success(let apiSuccess):
        // apiSuccess encoded to HTTPResponse
        return HTTPResponse()
    case .failure(let apiFailure):
        // apiFailure encoded to HTTPResponse
        return HTTPResponse()
    }
}

router.on("/ok") { request -> Result<Endpoint1Success, Endpoint1Failure> in
    return .success(.ok)
}

router.on("/not") { request -> Result<Endpoint1Success, Endpoint1Failure> in
    return .failure(.not)
}

也许不是正确的方法?

2 个答案:

答案 0 :(得分:0)

您要尝试的是通用差异,它仅在数组中本地受支持。

因此,这是您的用法示例的解决方法。但是在此之前,您的示例需要首先修复,因为它目前尚未编译。 ApiFailures应该是符合Error的类,而Endpoint1Failure也应该是一个类。其大小写应重写为static let

解决方法是仅使用非通用on方法,并按如下方式编写调用方:

router.on("/ok") { request in
    return Result<Endpoint1Success, Endpoint1Failure>.success(.ok)
        .map { $0 }.mapError { $0 }
}

本质上,您在每个return语句之后添加.map { $0 }.mapError { $0 }。这就是您从Result<S, F>投射到Result<ApiSuccess, ApiFailure>的方式。如果您不想一直写,可以将其提取为扩展名:

extension Result where Success : ApiSuccess, Failure : ApiFailure {
    func toGeneralApiResult() -> Result<ApiSuccess, ApiFailure> {
        map { $0 }.mapError { $0 }
    }
}

即使您可以限制SF参数,这种转换实际上也是不可避免的。唯一的区别是您在哪里进行此投射。如果可以约束SF,则可以在将result传递给encoder之前进行强制转换。在这种解决方法中,您只是在呼叫方进行操作。

答案 1 :(得分:0)

经过长时间的研究,我发现这很关键: https://github.com/apple/swift/pull/13012 和'广义超类型约束”看起来很必要。 如我所见,已于2019年11月19日接受(或不接受)实施,结果应等待Swift 6 +。