Как я могу использовать try с оператором объединения?

#swift

#swift

Вопрос:

Я пытаюсь присвоить значение x из функции f , которая принимает один параметр (строку) и выдает.

Текущая область выдает, поэтому я считаю, что do catch не требуется.

Я пытаюсь использовать try с оператором объединения ?? , но я получаю эту ошибку: 'try' cannot appear to the right of a non-assignment operator .

 guard let x = try f("a") ??
              try f("b") ??
              try f("c") else {
    print("Couldn't get a valid value for x")
    return
}
  

Если я изменю try на try? :

 guard let x = try? f("a") ??
              try? f("b") ??
              try? f("c") else {
    print("Couldn't get a valid value for x")
    return
}
  

Я получаю предупреждение Left side of nil coalescing operator '??' has non-optional type 'String??', so the right side is never used и ошибку: 'try?' cannot appear to the right of a non-assignment operator .

Если я помещаю каждую попытку? в скобках:

 guard let x = (try? f("a")) ??
              (try? f("b")) ??
              (try? f("c")) else {
    print("Couldn't get a valid value for x")
    return
}
  

Он компилируется, но x является необязательным, и я бы хотел, чтобы его развернули.

если я удалю вопросительные знаки:

 guard let x = (try f("a")) ??
              (try f("b")) ??
              (try f("c")) else {
    print("Couldn't get a valid value for x")
    return
}
  

Я получаю сообщение об ошибке Operator can throw but expression is not marked with 'try' .

Я использую Swift 4.2 (последняя версия Xcode на момент написания).

Каков правильный способ сделать это, чтобы получить развернутое значение в x ?

Обновление: возвращаемый тип * f() — String?. Я думаю, важен тот факт, что это необязательная строка.

Комментарии:

1. Я думаю, что вам следует начать с удаления дубликатов и сделать что-то вроде let x = ['a', 'b', 'c'].lazy.compactMap { try? f($0) }.first .

2. Одно try может охватывать все выражение, например guard let x = try f("a") ?? f("b") ?? f("c") else {...}

3. @Hamish Ваше предложение исправляет мою ошибку и делает мой код более читаемым. Спасибо! Пожалуйста, не стесняйтесь публиковать это в качестве ответа.

4. @JoshParadroid Конечно!

5. @Sulthan Ах да, да, это правильно. Извините, когда вы сказали «исходный код», я предположил, что вы имели в виду первый блок кода в вопросе OP. Насколько я понимаю, try поведение — это то, чего хочет OP.

Ответ №1:

Одно try может охватывать все выражение, поэтому вы можете просто сказать:

   guard let x = try f("a") ?? f("b") ?? f("c") else {
    print("Couldn't get a valid value for x")
    return
  }
  

То же самое касается try? :

   guard let x = try? f("a") ?? f("b") ?? f("c") else {
    print("Couldn't get a valid value for x")
    return
  }
  

Хотя обратите внимание, что в Swift 4.2 это x будет String? связано с тем, что вы применяете try? к уже необязательному значению, что дает вам дважды обернутое необязательное значение, которое guard let развернет только один слой.

Чтобы исправить это, вы могли бы объединить в nil :

   guard let x = (try? f("a") ?? f("b") ?? f("c")) ?? nil else {
    print("Couldn't get a valid value for x")
    return
  }
  

Но в Swift 5 в этом нет необходимости из-за SE-0230, где try? f("a") ?? f("b") ?? f("c") компилятор автоматически сведет в одно необязательное значение.

Комментарии:

1. Альтернативой для Swift < 5 был бы (двойной) необязательный шаблон: guard case let x?? = try? f("a") ?? f("b") ?? f("c")