В этом случае исключение, выброшенное в блоке отсрочки, может быть добавлено как исключенное исключение к исключению, выданному body()
, если таковое имеется. Есть ли у Swift аналогичная функция?
Не то, что я знаю - вы могли бы, однако, построить нечто подобное. Во-первых, давайте определим тип Error
, который может хранить несколько основных ошибок:
struct ErrorCollection : Error {
private var errors: [Error] = []
init() {}
init<S : Sequence>(_ sequence: S) where S.Element == Error {
for error in sequence {
append(error)
}
}
mutating func append(_ error: Error) {
switch error {
case let x as ErrorCollection: // ensure we flatten out any nested error collections.
errors.append(contentsOf: x.errors)
case let x:
errors.append(x)
}
}
}
extension ErrorCollection : RandomAccessCollection {
typealias Index = Int
typealias Element = Error
var startIndex: Index { return errors.startIndex }
var endIndex: Index { return errors.endIndex }
func index(_ i: Index, offsetBy n: Index) -> Index {
return errors.index(i, offsetBy: n)
}
subscript(index: Index) -> Element { return errors[index] }
}
Затем мы можем определить тип Result<T>
, который мы можем использовать для оценки замыкания, сохраняя возвращаемое значение в случае успеха, в противном случае выдается ошибка. Затем мы можем добавить метод then(_:)
, чтобы позволить нам перехватывать любые дополнительные ошибки, которые могут привести к тому, что значение успеха будет признано недействительным:
enum Result<T> {
case success(T)
case failure(Error)
init(_ body: () throws -> T) {
do {
self = .success(try body())
} catch {
self = .failure(error)
}
}
func then(_ body: () throws -> Void) -> Result {
do {
try body()
return self
} catch let nextError {
switch self {
case .success: // invalidate the success value and store the error.
return .failure(nextError)
case .failure(let error): // concatenate the errors.
return .failure(ErrorCollection([error, nextError]))
}
}
}
func materialize() throws -> T {
switch self {
case .success(let value):
return value
case .failure(let error):
throw error
}
}
}
Затем вы можете использовать это так:
func usingTemporaryDirectory<R>(body: (URL) throws -> R) throws -> R {
let tempDir = try createTemporaryDirectory()
return try Result { try body(tempDir) }
.then { try removeDirectory(tempDir) }
.materialize()
}