首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么守卫要破坏类型推断?

为什么守卫要破坏类型推断?
EN

Stack Overflow用户
提问于 2018-05-31 21:02:33
回答 3查看 172关注 0票数 2

我遇到了一个问题,就像标题所描述的那样:由于某种原因,guard语句破坏了类型推断。我已经创建了一个Playground项目来解决这个问题。

下面是一些用于设置的样板代码:

代码语言:javascript
复制
import Foundation

struct Model: Decodable {
    var i: String
}

let jsonString = """
{
    "i": "yes"
}
"""

let jsonData = jsonString.data(using: .utf8)
let theModel = try JSONDecoder().decode(Model.self, from: jsonData!)

struct Response<T> {
    var decodedData: T?
}

enum Result<Value> {
    case success(Value)
    case failure
}

struct Client {
    static let shared = Client()
    private init() {}

    func execute<T: Decodable>(completion: (Response<T>) -> ()) {
        let decodedData = try! JSONDecoder().decode(T.self, from: jsonData!)
        completion(Response(decodedData: decodedData))
    }
}

以下是问题所在:

代码语言:javascript
复制
struct ServicesA {
    func loadSomething(completion: (Result<Model>) -> ()) {
        Client.shared.execute { result in      // error: generic parameter 'T' could not be inferred
            guard let decodedData = result.decodedData else { return }
            completion(Result.success(decodedData))
        }
    }
}

struct ServicesB {
    func loadSomething(completion: (Result<Model>) -> ()) {
        Client.shared.execute { result in
            completion(Result.success(result.decodedData!))
        }
    }
}

ServicesA会中断,而ServicesB会编译。

正如您所看到的,唯一的区别是guard let decodedData = result.decodedData else { return }。它破坏了类型推断,因此Client.shared.execute函数会报告无法推断出T

我想知道为什么会发生这种情况,以及处理这个问题最合适的方法是什么。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-06-01 05:04:25

TLDR;单行闭包编译不同于多行

长答案:让我们暂时忘记单行闭包。当我们编写接受泛型作为参数类型的泛型函数,然后调用这个函数时,编译器需要在调用时知道所需的函数类型,即它的参数类型。现在考虑这个论点是一个闭包。同样,编译器需要知道调用时闭包的类型(函数签名)。这些信息应该在闭包的签名(即参数和返回类型)中可用,编译器并不担心闭包的主体(这是正确的)。因此,服务A的行为完全符合编译器的预期,即闭包签名没有给出任何有关其类型的线索。

现在有了单行闭合。Swift编译器有一个内置的类型推断优化,仅用于单行闭包。当你编写单行闭包时,编译器首先会启动它的类型推断,并试图从它唯一的一行主体中找出闭包,包括它的返回类型等。这种类型推断适用于单行闭包(带或不带泛型上下文)。这种类型推断是服务B工作的原因

因此,我将这个问题重新表述为“为什么类型推断适用于单行闭包?”因为这是Swift仅为单行闭包提供的额外功能。一旦你转移到多行闭包(它不是特定于guard语句的,如果你放入print(“hello world”)也会发生同样的事情),这种类型推断就不再可用。尽管可能有其他类型推断检查可用于多行闭包。

您所能做的就是在闭包签名中提供类型信息,即(结果: Response<模型>)

票数 4
EN

Stack Overflow用户

发布于 2018-05-31 21:36:03

当闭包中只有一条语句时,编译器才会推断闭包返回类型。

代码语言:javascript
复制
let x = {
    return 1
}()

                // x == Int(1)

let y = {       // error: unable to infer complex closure return type; add explicit type to disambiguate
    print("hi")
    return 2
}()

https://forums.swift.org/t/problems-with-closure-type-inference/11859/2

票数 3
EN

Stack Overflow用户

发布于 2018-05-31 21:18:38

问题是您引用的是data变量,但是编译器不知道这是什么类型(因为它是泛型)。试试这个:

代码语言:javascript
复制
struct ServicesA {
    func loadSomething<T:Model>(completion:(Result<T>) -> ()) {
        Client.shared.execute { result:Response<T> in
            guard let decodedData = result.decodedData else { return }
            completion(Result.success(decodedData))
        }
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50625011

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档