快速,可选展开,如果条件可逆

时间:2014-10-10 17:33:44

标签: swift error-handling optional

我们说我有返回可选的功能。如果成功则错误和价值为零:

func foo() -> Bar? { ... }

我可以使用以下代码来使用此功能:

let fooResultOpt = foo()
if let fooResult = fooResultOpt {
    // continue correct operations here
} else {
    // handle error
}

然而,对于任何非平凡的代码,这种方法几乎没有问题:

  1. 最终执行的错误处理很容易错过。当错误处理代码跟随函数调用时,它会好得多。

  2. 正确的操作代码缩进一级。如果我们有另一个函数要调用,我们必须再缩进一次。

  3. 使用C,通常可以写出这样的内容:

    Bar *fooResult = foo();
    if (fooResult == null) {
        // handle error and return
    }
    // continue correct operations here
    

    我找到了两种使用Swift实现类似代码风格的方法,但我也不喜欢。

    let fooResultOpt = foo()
    if fooResult == nil {
        // handle error and return
    }
    // use fooResultOpt! from here
    let fooResult = fooResultOpt! // or define another variable
    

    如果我要写"!"在任何地方,它只是看起来不好我的口味。我可以介绍另一个变量,但这看起来也不好看。理想情况下,我希望看到以下内容:

    if !let fooResult = foo() {
        // handle error and return
    }
    // fooResult has Bar type and can be used in the top level
    

    我是否错过了规范中的某些内容,或者是否有其他方法可以编写好看的Swift代码?

5 个答案:

答案 0 :(得分:2)

你的假设是正确的 - 没有"否定if-let" Swift中的语法。

我怀疑其中一个原因可能是语法完整性。在整个Swift(通常在其他C语言中),如果你有一个可以绑定局部符号的语句(即命名新变量并赋予它们值)并且可以有一个块体(例如if,while,for),那些绑定范围限定为所述块。让一个块语句将符号绑定到其封闭的范围将是不一致的。

但是,考虑一下这仍然是一个合理的事情 - 我建议filing a bug并看看Apple对此有何看法。

答案 1 :(得分:2)

这就是模式匹配的全部内容,也是这项工作的工具:

let x: String? = "Yes"

switch x {
case .Some(let value):
  println("I have a value: \(value)")
case .None:
  println("I'm empty")
}

当你不需要双腿时,if-let形式只是一种便利。

答案 2 :(得分:1)

如果您正在编写的是一组执行相同转换序列的函数,例如处理REST调用返回的结果时(检查响应不是nil,检查状态,检查app / server错误,解析响应)等等),我要做的是创建一个管道,在每个步骤转换输入数据,最后返回nil或某种类型的转换结果。

我选择了>>>自定义运算符,它可以直观地显示数据流,但当然可以自由选择:

infix operator >>> { associativity left }

func >>> <T, V> (params: T?, next: T -> V?) -> V? {
    if let params = params {
        return next(params)
    }
    return nil
}

运算符是一个函数,它接收某种类型的值作为输入,以及一个将值转换为另一种类型值的闭包。如果值不是nil,则函数调用闭包,传递值,并返回其返回值。如果值为nil,则运算符返回nil

可能需要一个例子,所以让我假设我有一个整数数组,我想按顺序执行以下操作:

  • 汇总数组的所有元素
  • 计算2的力量
  • 除以5并返回整数部分和余数
  • 将上述两个数字相加

这些是4个功能:

func sumArray(array: [Int]?) -> Int? {
    if let array = array {
        return array.reduce(0, combine: +)
    }

    return nil
}

func powerOf2(num: Int?) -> Int? {
    if let num = num {
        return num * num
    }
    return nil
}

func module5(num: Int?) -> (Int, Int)? {
    if let num = num {
        return (num / 5, num % 5)
    }
    return nil
}

func sum(params: (num1: Int, num2: Int)?) -> Int? {
    if let params = params {
        return params.num1 + params.num2
    }
    return nil
}

这就是我的用法:

let res: Int? = [1, 2, 3] >>> sumArray >>> powerOf2 >>> module5 >>> sum

此表达式的结果是nil或管道的最后一个函数中定义的类型的值,在上面的示例中为Int

如果您需要做更好的错误处理,可以定义这样的枚举:

enum Result<T> {
    case Value(T)
    case Error(MyErrorType)
}

并使用Result<T>替换上述函数中的所有可选项,并返回Result.Error()而不是nil

答案 3 :(得分:1)

我发现了一种看起来比替代方案更好的方法,但它以未经推荐的方式使用语言功能。

使用问题代码的示例:

let fooResult: Bar! = foo();
if fooResult == nil {
    // handle error and return
}
// continue correct operations here

fooResult可以用作普通变量,不需要使用“?”要么 ”!”后缀。

Apple文档说:

  

如果在首次定义可选项后立即确认可选项的值存在,并且可以假定在此后的每个点都存在,则隐式解包的选项会很有用。 Swift中隐式解包选项的主要用途是在类初始化期间,如Unowned References和Implicitly Unwrapped Optional Properties中所述。

答案 4 :(得分:0)

以下内容如何:

func foo(i:Int) ->Int? {
    switch i {
    case 0: return 0
    case 1: return 1
    default: return nil
    }
}

var error:Int {
    println("Error")
   return 99
}

for i in 0...2 {
    var bob:Int = foo(i) ?? error
    println("\(i) produces \(bob)")
}

结果如下:

0 produces 0
1 produces 1
Error
2 produces 99