0

I'm doing something really simple just to get used to Swift (coming from objc) - I want to return a desired node in a linked list by using a guard statement and a switch statement. I'm obviously misusing the guard statement because my else clause is huge (it's where my switch statement is being kept). Perhaps I don't even need a switch statement, but it just cleans things up a bit.

My old code is as follows:

func getValue (atIndex index: Int) -> T {
    if count < index || index < 0 {
        print ("index is outside of possible range")
    }
    var root = self.head
    //        if var root = self.head {
    if index == 0 {
        return (self.head?.value)!
    }
    if index == count-1 {
        return (self.tail?.value)!
    }
    else {
        for _ in 0...index-1 {
            root = root!.next!
        }
    }
    return root!.value
}

Replaced with a guard statement (but getting a compiler error that the guard body may not fall through) - my problem is what to return, since my function return type is <T> (any type).

func getValue (atIndex index: Int) -> T {
    guard  (count < index || index < 0) else {
        switch true {
        case index == 0:
            if let head = self.head {
                return head.value
            }
        case index == count-1:
            if let tail = self.tail {
                return tail.value
            }
        default:
            if var currentNode = head {
                for _ in 0...index-1 {
                    currentNode = currentNode.next!
                }
                return currentNode.value
            }
        }
    }
}

I'd like to add a print statement outside of my guard statement saying that the desired index is outside of the range, but I also need to return something at the end of the function of type T. The problem is that outside of my guard and switch statement, I have nothing to return.

David Snabel
  • 9,801
  • 6
  • 21
  • 29
karan satia
  • 307
  • 4
  • 16
  • 3
    I think you've misunderstood the usage of guards. The condition that you put in the guard is the condition for *success*. The stuff you place in the body of the guard is the code to execute upon *failure*, which must lead to a transfer of control (`return`, `throw` etc). Therefore the success code should come just after the guard. Also switching over `true` makes no sense, surely switching over `index` would make more sense since that's what you're comparing? Also what's `T`? Your function isn't generic, is it a class generic? – Hamish May 25 '16 at 20:48
  • When I tried switching over index, I was met with the error message "expression pattern of type 'Bool' cannot match values of type 'Int'". The Linked List class is generic - when instantiating, I specify what type of object I want each node value to be. – karan satia May 25 '16 at 20:54
  • You need to use the possible values of your `index` as the cases, i.e `case 0`, `case count-1`. – Hamish May 25 '16 at 21:35
  • You should accept CharlesA's answer, but a few unrelated observations: First, `getValue` would appear to be retrieving an item from a linked list. Rather than fixing the name to conform to standards (like CharlesA did, renaming it to `getValueAtIndex`), I'd just make it a `subscript`. Second, `switch true {...}` is redundant. I'd actually lose the `if` statements and use meaningful `case` values in `switch index { ... }`. Third, you have some paths in your code where you don't return value. For example, see https://gist.github.com/robertmryan/4286f243169b1394f0f31d4659a03e5b. – Rob May 26 '16 at 00:11

2 Answers2

2

The guard statement is used to catch the invalid case, so you'd want something like:

func getValueAtIndex(index: Int) -> T {
    guard index >= 0 && index < count else {
        // Invalid case
        print("Index is outside of possible range")

        // Guard must return control or call a noreturn function.
        // A better choice than the call to fatalError might be
        // to change the function to allow for throwing an exception or returning nil.
        fatalError("Index out of bounds")
    }

    // Valid cases
}
Mat
  • 202,337
  • 40
  • 393
  • 406
Charles A.
  • 10,685
  • 1
  • 42
  • 39
0

A guard statement is meant to move the program out of its current scope or call a noreturn function if a value is found to be nil. You however, are running an entire switch statement in your guard.

As per Apple's Guard Documentation:

The else clause of a guard statement is required, and must either call a function marked with the noreturn attribute or transfer program control outside the guard statement’s enclosing scope using one of the following statements:

  • return
  • break
  • continue
  • throw

A good example of a guard might be:

var optValue : String?

guard let optValue = optValue else {return}
Community
  • 1
  • 1
ZGski
  • 2,398
  • 1
  • 21
  • 34