【问题标题】:Cannot explicitly specialize a generic function, multiple generics不能显式特化一个泛型函数,多个泛型
【发布时间】:2019-07-21 16:59:57
【问题描述】:

围绕我的问题有很多答案,但我为他们尝试的解决方案并没有完全解决问题。我不确定它是否与拥有多个泛型或其他东西有关(对于 Swift 来说也是较新的,所以我仍然在思考语法)。

我注意到我的 API 请求代码中有很多共同点,因此我决定将主要工作抽象出来,让它与任何可编码的请求/响应对象一起工作。这是我的主要请求方法:

private func sendRequest<T: Encodable, U: Decodable>(url: URL, requestModel: T) -> Promise<U> 

我试图这样称呼它:

public func signIn(requestModel: SignInRequest) -> Promise<SignInResponse> {
    let url = URL(string: authURL + "/signin")!

    //getting compile error "Cannot explicitly specialize generic function" on this line
    return sendRequest<SignInRequest, SignInResponse>(url: url, requestModel: requestModel)
}

我试过直接赋值给返回对象:

let x : Promise<SignInResponse> = sendRequest(...)
return x

帮助编译器(如其他解决方案中所建议的那样)但仍然是同样的问题。有什么见解吗?

【问题讨论】:

  • 即使在将sendRequest 的结果分配给显式类型的变量后编译仍然失败,这有点令人惊讶。你能提供更多关于PromiseSignInRequestSignInResponse的信息吗?

标签: swift


【解决方案1】:

我最终使用了基于这篇文章的解决方案:https://stackoverflow.com/a/36232002/1088099

我明确地将请求和响应对象类型作为参数传入:

private func sendRequest<T: Encodable, U: Decodable>(requestModelType: T.Type, responseModelType: U.Type, url: URL, requestModel: T) -> Promise<U> 

并调用:

public func signIn(requestModel: SignInRequest) -> Promise<SignInResponse> {
    let url = URL(string: authURL + "/signin")!

    return sendRequest(requestModelType:SignInRequest.self,
                       responseModelType:SignInResponse.self,
                       url: url,
                       requestModel: requestModel)
}

不像我希望的那样干净,但有效。

【讨论】:

    【解决方案2】:

    如果您为函数的参数提供足够的上下文以确定所有泛型参数,推理就会成功。

    根据您的代码,我怀疑情况并非如此,因为泛型类型 U 既没有作为参数出现,也没有额外的类型约束来指定它。因此,编译器无法从函数调用中推断出函数返回值的具体类型。

    因此,如果TU 之间存在关系,则可以将其用作附加约束,以“帮助”编译器在找到U 后推断U。这是一个最小的例子:

    protocol Foo {
      associatedtype Bar
      var bar: Bar { get }
    }
    
    struct FooImpl: Foo {
      let bar: Int = 0
    }
    
    func f<T, U>(foo: T) -> U where T: Foo, U == T.Bar  {
      return foo.bar
    }
    
    let a = f(foo: FooImpl())
    

    协议Foo 有一个关联类型Bar。通过在函数f 的签名中使用此关联来为其返回类型添加约束,当我使用FooImpl 的实例调用它时,Swift 的编译器现在能够完全推断出f 的专用签名。


    在您的特定示例中,我建议编写一个协议,requestModel 参数应确认该协议,并使用关联类型对返回值施加约束。 例如:

    protocol RequestModel: Encodable {
      associatedtype ResponseModel: Decodable
    }
    
    struct AnAwesomeModel: RequestModel {
      typealias ResponseModel = String
    }
    
    private func sendRequest<T: RequestModel, U>(url: URL, requestModel: T) -> Promise<U>
      where U == T.ResponseModel
    {
      // ...
    }
    
    // x's type will be inferred as String
    let x = sendRequest(url: URL("http://...")!, requestModel: AnAwesomeModel())
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-18
      • 2019-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多