Hamp 2025. 9. 22. 13:14
반응형

🥊 rethrows

 

🎯 목표

아래 상황과 같이, 클로저에서 에러를 던질 때(throw)할 때, 어떻게 던질 수 있을까?

enum CustomError: Error {
    case divideByZero
}

func divide(number: Int, divisor: Int) throws -> Int {
    if divisor == .zero {
        throw CustomError.divideByZero
    }
    
    return number / divisor
}

func calculate(function: (Int, Int) throws -> Int) {
    throw function(10, 0) // ⚠️ Thrown expression type 'Int' does not conform to 'Error'
}

try calculate(function: divide)

 

⭐️ 역할

함수 파리미터에 throws 키워드가 있고, 에러를 현재 스코프가 아닌, caller쪽에 전달하고 싶을 떄 사용

 

😀 해결

enum CustomError: Error {
    case divideByZero
}

func divide(number: Int, divisor: Int) throws -> Int {
    if divisor == .zero {
        throw CustomError.divideByZero
    }
    
    return number / divisor
}

func calculate(function: (Int, Int) throws -> Int) rethrows {
    print(try function(10, 0))
}

do {
    try calculate(function: divide)
} catch {
    print("Error: \(error)")
}

 

 

✨ 특징

  • rethrows 자리에 throws를 붙혀도 동작은 똑같다. 다만, 현재 함수에서 발생하는 erorr면 throws,
    위와 같이 파라미터로 전달된 에러일 경우는 retrhows로 표시해, 구분하는 것이 좋다고한다.
  • 부모의 throws -> 자식의 rethorws로 재정의 ✅
  • 부모의 rethrows -> 자식의 throws로 재정의 ❌
  • 프로토콜의 throws -> 구현부 rethrows ✅
  • 프로토콜의 retrhows -> 구현부 throws ❌
// MARK: - 부모 → throws
class Parent {
    func doSomething(action: () throws -> Void) throws {
        try action()
    }
}

// ✅ 자식: throws → rethrows 로 완화 (허용)
class Child: Parent {
    override func doSomething(action: () throws -> Void) rethrows {
        print("Child: safely rethrows")
        try action()
    }
}

let parent: Parent = Child()

// Parent 타입이므로 호출자는 throws 기준으로 try 필요
try parent.doSomething {
    print("Executing action") // 실제 실행은 rethrows 구현이지만 여전히 try 필요
}

// ===============================================================================================================

class Base {
    func perform(action: () throws -> Void) rethrows {
        try action()
    }
}

 ❌ 컴파일 에러: 'throws'로 강화할 수 없음

class Sub: Base {
    override func perform(action: () throws -> Void) throws {
         Error: overriding rethrows with throws is not allowed
        try action()
    }
}

// ===============================================================================================================


// MARK: - 프로토콜 rethrows
protocol ReThrowingProtocol {
    func execute(_ action: () throws -> Void) rethrows
}

// ❌ throws 로 구현하면 오류
struct WrongImpl: ReThrowingProtocol {
    func execute(_ action: () throws -> Void) throws {
        // Error: 'throws' function cannot satisfy 'rethrows' requirement
        try action()
    }
}


// ✅ 올바른 구현: rethrows 유지
struct CorrectImpl: ReThrowingProtocol {
    func execute(_ action: () throws -> Void) rethrows {
        try action()
    }
}

// ===============================================================================================================

// MARK: - 프로토콜 throws
protocol ThrowingProtocol {
    func run(_ action: () throws -> Void) throws
}

// ✅ rethrows 로 구현해도 throws 요구사항 충족
struct RethrowImpl: ThrowingProtocol {
    func run(_ action: () throws -> Void) rethrows {
        try action()
    }
}

출처

 

Swift 3.0 의 throws, rethrows 에 대하여...

반년전즈음 스터디모임에서 Swift 3.0의 Array 에 대해 다루다가 map 이라는 함수를 보고 의문점이 생겼다. 분명히 내가 기억하기로는 Swift 2.2에서 Array의 map 함수는 Array.map(transform: T -> U) 이와 같이

redsubmarine.github.io

 

반응형