swift 如何在'guardcase'语句的'else'子句中只得到'.failure'?

68de4m5k  于 2023-03-11  发布在  Swift
关注(0)|答案(2)|浏览(112)

我想弄清楚如何让Swift推断出下面代码片段中guard caseelse子句中唯一可能的值是.failure,这是可能的,或者可能有其他方法可以采用?

enum MyError {
    case error
}

func create() {
    let result = createResult(...) // -> Result<Data, MyError>
        
    guard case let .success(resultData) = result else {
        // maybe somehow get the else case of guard stored in a variable in here, but how???
        switch result {
        case .failure(.error):
            self.isError = true
            return
        }
        // getting "Switch must be exhaustive" error even though .success is not possible and there is only one .error case in the error enum 
    }

    // success code here...
}

我尝试使用来自@HangarRash的answer的常规switch,它工作正常,但我想避免函数成功部分的嵌套。

eh57zj3b

eh57zj3b1#

即使嵌套的数量是可以忍受的,我仍然想知道是否有可能做到这一点没有嵌套。
嗯,如果你做一个扩展,大多数事情都是可能的:)

extension Result {
    func tryGetValue(onFailure block: (Failure) -> Void) -> Success? {
        switch self {
        case .success(let x):
            return x
        case .failure(let e):
            return nil
        }
    }
}

然后:

func create() {
    let result = createResult(...)
        
    guard let resultData = result.tryGetValue(onFailure: { error in
        // you can use the closure parameter "error" here...
        // check if it is MyError.error or whatever
        self.isError = true
    }) else { return } // you must return here

    // process resultData...
}

这看起来相当丑陋,虽然说实话,但确实,它不巢成功的分支。
我建议将其转换为使用常规的try...catch错误处理,方法是在Result上调用get

func create() {
    do {
        let result = try createResult(...).get()
        // process success result...
    } catch MyError.error {
// or catch every error of type MyError
//  } catch let error as MyError {
        self.isError = true
    } catch {} // unreachable
}

(If您可以更改createResult函数,只需将其更改为throws即可。)
如果您以相同的方式处理所有错误,则可以轻松地将其扩展到多个Result

func create() {
    do {
        let result1 = try createResult1(...).get()
        let result2 = try createResult2(...).get()
        let result3 = try createResult3(...).get()
        // process success result...
    } catch {
        self.isError = true
    }
}

但是,这样做的缺点是,如果多个Result具有相同的故障类型,则您将不知道错误来自catch块中的何处,这意味着您无法以不同的方式处理这些结果。

tcbh2hod

tcbh2hod2#

由于您需要处理这两种可能的情况,因此guard没有任何意义,只需在switch中填写这两种情况,并在.success中处理成功代码即可。

func create() {
    let result = createResult(...) // -> Result<Data, MyError>
        
    switch result {
        case .success(let resultData):
            // success code here...
        case .failure(let error): // Handle any error code
            self.isError = true
    }
}

简单得多,也更有条理。如果“成功”代码又长又复杂,那么就把它放在一个单独的函数中。类似这样:

func create() {
    let result = createResult(...) // -> Result<Data, MyError>
        
    switch result {
        case .success(let resultData):
            process(data: resultData)
        case .failure(let error): // Handle any error code
            self.isError = true
    }
}

private func process(data: Data) {
    // complicated data processing
}

如果您想保留guard,可以将switch替换为if case let,因为此时您只需要处理.failure情况:

func create() {
    let result = createResult(...) // -> Result<Data, MyError>
        
    guard case let .success(resultData) = result else {
        if case let .failure(error) = result {
            // print(error) // this will always be .error in your setup
            self.isError = true
        }
        return
    }

    // success code here using resultData
}

相关问题