如何判断哪个警卫声明失败?

Mos*_*she 15 error-handling control-flow swift

如果我有一堆链式守卫让我们发表声明,我怎么能诊断出哪个条件失败了,不能将我的警卫分成多个陈述?

鉴于这个例子:

guard let keypath = dictionary["field"] as? String,
    let rule = dictionary["rule"] as? String,
    let comparator = FormFieldDisplayRuleComparator(rawValue: rule),
    let value = dictionary["value"]
    else
    {
        return nil
    }
Run Code Online (Sandbox Code Playgroud)

如何判断4个let语句中哪个是失败并调用了else块?

我能想到的最简单的事情是将语句分成4个连续的保护其他语句,但这感觉不对.

 guard let keypath = dictionary["field"] as? String
    else
    {
        print("Keypath failed to load.")

        self.init()
        return nil
    }

    guard let rule = dictionary["rule"] as? String else
    {
        print("Rule failed to load.")

        self.init()
        return nil
    }

    guard let comparator = FormFieldDisplayRuleComparator(rawValue: rule) else
    {
        print("Comparator failed to load for rawValue: \(rule)")

        self.init()
        return nil
    }

    guard let value = dictionary["value"] else
    {
        print("Value failed to load.")

        self.init()
        return nil
    }
Run Code Online (Sandbox Code Playgroud)

如果我想把它们全部放在一个警卫声明中,我可以想到另一种选择.检查guard语句中的nils可能有效:

guard let keypath = dictionary["field"] as? String,
    let rule = dictionary["rule"] as? String,
    let comparator = FormFieldDisplayRuleComparator(rawValue: rule),
    let value = dictionary["value"]
    else
    {

        if let keypath = keypath {} else {
           print("Keypath failed to load.")
        }

        // ... Repeat for each let...
        return nil
    }
Run Code Online (Sandbox Code Playgroud)

我甚至不知道是否会编译,但是我可能还会使用一堆if let语句或者guards开头.

什么是惯用的Swift方式?

kei*_*ter 12

Erica Sadun刚刚写了一篇关于这个话题的好文章.

她的解决方案是对这个where条款进行hi-jack 并使用它来跟踪哪些保护语句通过.使用该diagnose方法的每个成功保护条件都会将文件名和行号打印到控制台.最后一个diagnoseprint语句后面的保护条件是失败的.解决方案看起来像这样:

func diagnose(file: String = #file, line: Int = #line) -> Bool {
    print("Testing \(file):\(line)")
    return true
}

// ...

let dictionary: [String : AnyObject] = [
    "one" : "one"
    "two" : "two"
    "three" : 3
]

guard
    // This line will print the file and line number
    let one = dictionary["one"] as? String where diagnose(),
    // This line will print the file and line number
    let two = dictionary["two"] as? String where diagnose(),
    // This line will NOT be printed. So it is the one that failed.
    let three = dictionary["three"] as? String where diagnose()
    else {
        // ...
}
Run Code Online (Sandbox Code Playgroud)

Erica关于这个主题的文章可以在这里找到


jtb*_*des 8

通常,guard声明不允许您区分哪些条件不满足.它的目的是当程序执行超过 guard语句时,您知道所有变量都是非零的.但是它没有在防护/ 身体内部提供任何值else(你只知道条件并非完全满足).

也就是说,如果您想要做的只是print其中一个步骤返回的内容nil,您可以使用合并运算符??来执行额外的操作.

创建一个打印消息的泛型函数并返回nil:

/// Prints a message and returns `nil`. Use this with `??`, e.g.:
///
///     guard let x = optionalValue ?? printAndFail("missing x") else {
///         // ...
///     }
func printAndFail<T>(message: String) -> T? {
    print(message)
    return nil
}
Run Code Online (Sandbox Code Playgroud)

然后使用此函数作为每个案例的"后备".由于??操作员采用短路评估,除非左侧已经返回零,否则不会执行右侧.

guard
    let keypath = dictionary["field"] as? String ?? printAndFail("missing keypath"),
    let rule = dictionary["rule"] as? String ?? printAndFail("missing rule"),
    let comparator = FormFieldDisplayRuleComparator(rawValue: rule) ?? printAndFail("missing comparator"),
    let value = dictionary["value"] ?? printAndFail("missing value")
else
{
    // ...
    return
}
Run Code Online (Sandbox Code Playgroud)