2

I implemented the following function -as an extension of array of booleans- which could throw a CustomError error:

enum CustomError: Error {
    case empty
    case doesNotContainTrue
}

extension Array where Element == Bool {
    func indexOfFirstTrue() throws -> Int {
        if isEmpty { throw CustomError.empty }

        guard let detectedIndex = index(of: true) else {
            throw CustomError.doesNotContainTrue
        }

        return detectedIndex
    }
}

which works as expected:

let myArray = [false, true, false, true]
try print(myArray.indexOfFirstTrue()) // 1

Next, I tried to declare a function as:

func handleResult(_ index: Int) throws {
    print(index * 2)
    // ...
}

which should take the result of myArray.indexOfFirstTrue() and do something with it (for simplicity, let's assume that it prints the value multiplied by 2):

try handleResult(myArray.indexOfFirstTrue()) // 2

What I want to do is to declare handleResult as rethrowing function:

A function or method can be declared with the rethrows keyword to indicate that it throws an error only if one of its function parameters throws an error. These functions and methods are known as rethrowing functions and rethrowing methods. Rethrowing functions and methods must have at least one throwing function parameter.

The Swift Programming Language (Swift 4.1): Declarations - Rethrowing Functions and Methods.

So I can call it with non-throwing formula, thus it will not throws an error:

handleResult(myArray.indexOfFirstTrue()) // 2

But I am stuck of what should I edit to let it be a rethrowing function, so I tried to declare it as:

func handleResult(_ index: Int) rethrows {
    print(index * 2)
}

and I got the error of:

error: 'rethrows' function must take a throwing function argument

therefore, I also tried to declare it as:

func handleResult(_ index: (() throws ->  Int)) rethrows {
    print(index * 2)
}

and obviously got the error of:

error: cannot convert value of type 'Int' to expected argument type '() throws -> Int'

What should I do at this point?

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
  • Why should `handleResult` be rethrowing? As far as I can tell, you just want `indexOfFirstTrue` to throw. You would still call it as `try handleResult(myArray.indexOfFirstTrue())`. – Hamish Apr 05 '18 at 12:58
  • 1
    Unrelated, but your logic to get the `detectedIndex` can be simplified to `guard let detectedIndex = index(of: true) else { throw CustomError.doesNotContainTrue }`. – Hamish Apr 05 '18 at 13:00
  • Thanks for the note @Hamish :) – Ahmad F Apr 05 '18 at 13:05

1 Answers1

2

Remember, the argument is of type () -> Int! So you need to call the function passed in to get the result! You also need try since the function can throw.

func handleResult(_ index: (() throws ->  Int)) rethrows {
    print(try index() * 2) // see the "()"?
}

Now you can use it like this:

let myArray = [true]
try handleResult(myArray.indexOfFirstTrue)
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Thanks mate. based on your answer, I still have to call it with the `try` (`try handleResult(myArray.indexOfFirstTrue)`), which is what I am trying to avoid. So what should I do the be able to call is as `handleResult(myArray.indexOfFirstTrue)`? – Ahmad F Apr 05 '18 at 13:01
  • @AhmadF Then you have to handle the error inside `handleResult`, and remove `rethrows`. – Sweeper Apr 05 '18 at 13:02
  • What you mentioned seems to be logical to me, However, I would like to know- then how the logic of `map` function works?! as mentioned in [this answer](https://stackoverflow.com/a/43305215/5501940), `map` can take a non-throwing function, which is not allowed in my case, if we tried to: `func getValue() -> Int { return 2 } handleResult(getValue())` it won't work. appreciate your kind patience – Ahmad F Apr 05 '18 at 13:13
  • @AhmadF You should pass `getValue` without the brackets: `handleResult(getValue)` Remember you are not calling the function! Also, you can write it like this `handleResult { 2 }` – Sweeper Apr 05 '18 at 13:15
  • omg! oh man I just did the same mistake... That's what I meant of: I want to call it without the `try` (`handleResult { 1 }`). – Ahmad F Apr 05 '18 at 13:25