gpt4 book ai didi

可以抛出和不能抛出的方法中的 Swift 类型推断

转载 作者:搜寻专家 更新时间:2023-10-31 08:17:18 32 4
gpt4 key购买 nike

如您所知,Swift 可以根据用法推断类型。例如,您可以拥有仅在返回类型上不同的重载方法,并且只要编译器能够推断类型就可以自由使用它们。例如,在附加显式类型变量的帮助下,该变量将保存此类方法的返回值。

我发现了一些有趣的时刻。想象一下这个类:

class MyClass {
enum MyError: Error {
case notImplemented
case someException
}

func fun1() throws -> Any {
throw MyError.notImplemented
}

func fun1() -> Int {
return 1
}

func fun2() throws -> Any {
throw MyError.notImplemented
}

func fun2() throws -> Int {
if false {
throw MyError.someException
} else {
return 2
}
}
}

当然,它会像这样工作:

let myClass = MyClass()
// let resul1 = myClass.fun1() // error: ambiguous use of 'fun1()'
let result1: Int = myClass.fun1() // OK

但是接下来你可以这样写:

// print(myClass.fun1()) // error: call can throw but is not marked with 'try'
// BUT
print(try? myClass.fun1()) // warning: no calls to throwing functions occur within 'try' expression

所以看起来像是互斥语句。编译器尝试选择正确的函数;第一次调用时,它试图强制从 Int 转换为 Any,但它试图对第二次调用做什么?

此外,代码如下

if let result2 = try? myClass.fun2() { // No warnings
print(result2)
}

将没有警告,因此人们可能会假设编译器能够在这里选择正确的重载(可能基于事实,其中一个重载实际上什么都不返回,只抛出异常)。

我最后的假设是否正确? fun1() 的警告是否符合逻辑?我们是否有一些技巧可以愚弄编译器或帮助它进行类型推断?

最佳答案

显然,您永远不应该编写这样的代码。它有太多的方式可以咬你,正如你所看到的那样。但让我们看看为什么。

首先,try在Swift中只是一个装饰。它不适用于编译器。这是给你的。编译器计算出所有类型,然后确定是否需要 try。它不使用 try 来确定类型。您可以在此处实际看到这一点:

class X {
func x() throws -> X {
return self
}
}

let y = try X().x().x()

您只需要try 一次,即使链中有多个抛出调用。想象一下,如果您根据抛出与非抛出在 x() 上创建重载,这将如何工作。答案是“没关系”,因为编译器不关心 try

接下来是类型推断与类型强制的问题。这是类型推断:

let resul1 = myClass.fun1() // error: ambiguous use of 'fun1()'

Swift 永远不会推断出有歧义的类型。这可能是 Any 也可能是Int`,所以它放弃了。

这不是类型推断(类型已知):

let result1: Int = myClass.fun1() // OK

这也有一个已知的、明确的类型(注意没有 ?):

let x : Any = try myClass.fun1()

但这需要类型强制(很像您的打印示例)

let x : Any = try? myClass.fun1() // Expression implicitly coerced from `Int?` to `Any`
// No calls to throwing function occur within 'try' expression

为什么这里调用Int 版本? try? 返回一个 Optional(这是一个 Any)。所以 Swift 在这里可以选择返回 Int? 并将其强制转换为 AnyAny? 并将其强制转换为 Any 。 Swift 几乎总是喜欢实类型而不是 Any(而且它讨厌 Any?)。这是在代码中避免 Any 的众多原因之一。它以奇怪的方式与 Optional 交互。有争议的是,这应该是一个错误,但是 Any 是一种非常奇怪的类型,很难确定它的所有极端情况。

那么这如何应用于 printprint 的参数是Any,所以这就像let x: Any =... 的例子而不是let x =... 示例。

在考虑这些事情时要牢记一些自动强制转换:

  • 每个 T 都可以简单地强制转换为 T?
  • 每个 T 都可以显式强制转换为 Any
  • 每个 T?也可以显式强制为 Any
    • Any 可以被简单地强制转换为 Any? (还有 Any??、Any??? 和 Any????? 等)
    • 有吗? (Any??, Any???, etc) 可以显式强制转换为 Any
  • 每个非抛出函数都可以简单地强制转换为抛出版本
    • 因此,纯粹在“throws”上重载是危险的

所以将抛出/非抛出转换与 Any/Any 混合?转换,并将 try? 放入组合中(将所有内容提升为可选),你已经制造了一场完美的困惑 Storm 。

显然,您永远不应该编写这样的代码。

关于可以抛出和不能抛出的方法中的 Swift 类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48386707/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com