swift - 通过泛型约束泛型
问题描述
有人可以提供解决方案吗,正如我在底部所描述的那样,任何变体都无法通过(或不是???),但也许有人知道非常接近的解决方案?
PS 请参阅代码注释中的描述。
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)
}
或者也许不是正确的方法?
解决方案
What you are trying to do is generic variance, which is only natively supported for arrays.
So here's a workaround for your usage example. But before that, your example needs to be fixed first, as it doesn't currently compile. ApiFailures
should be a class conforming to Error
and Endpoint1Failure
should be a class as well. Its cases should be rewritten to static let
s.
The workaround is to only use the non-generic on
method, and write the caller like this:
router.on("/ok") { request in
return Result<Endpoint1Success, Endpoint1Failure>.success(.ok)
.map { $0 }.mapError { $0 }
}
Essentially, you add .map { $0 }.mapError { $0 }
after every return statement. This is the way you cast from Result<S, F>
to Result<ApiSuccess, ApiFailure>
. If you don't want to write this all the time, you can extract this into an extension:
extension Result where Success : ApiSuccess, Failure : ApiFailure {
func toGeneralApiResult() -> Result<ApiSuccess, ApiFailure> {
map { $0 }.mapError { $0 }
}
}
This cast is actually unavoidable, even if you could constrain your S
and F
parameters. The only difference is where you do this cast. If you could constrain S
and F
, you would cast result
before passing it to encoder
. In this workaround you're just doing it on the caller's side.
推荐阅读
- python - 套接字上的 BrokenPipeError
- c - 零大小数组在结构内未对齐
- javascript - 如何在按钮点击discord.js上发送DM
- python - Google Book API 错误“429 客户端错误:对 url 的请求过多”
- python - 装饰的异步方法返回 None
- typescript - Vue测试预期的空值
- c# - 使用 c# 套接字放置的服务器响应
- azure-devops - Power BI 如何提取工作项的标签
- python-3.x - 为字符串赋予十六进制颜色,以便与另一个字符串相似的字符串将获得相似的颜色
- java - 如何将数组添加到包含数组的动态数组中?