Неправильное использование оператора guard для замены нулевой проверки

Я делаю что-то действительно простое, чтобы привыкнуть к Swift (исходя из objc) - я хочу вернуть желаемый узел в связанном списке, используя оператор guard и оператор switch. Очевидно, я неправильно использую оператор guard, потому что мой оператор else огромен (именно там хранится мой оператор switch). Возможно, мне даже не нужен оператор switch, но он просто немного проясняет ситуацию.

Мой старый код выглядит следующим образом:

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
}

Заменено на оператор guard (но получаю ошибку компилятора, из-за которой тело защиты может не провалиться) - моя проблема в том, что возвращать, поскольку тип возвращаемого значения моей функции - <T> (любой тип).

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
            }
        }
    }
}

Я хотел бы добавить оператор print вне моего оператора guard, говорящий, что желаемый индекс находится за пределами диапазона, но мне также нужно что-то вернуть в конце функции типа T. Проблема в том, что за пределами моего оператора guard и switch мне нечего возвращать.


person karan satia    schedule 25.05.2016    source источник
comment
Я думаю, вы неправильно поняли использование охранников. Условие, которое вы ставите в охрану, является условием успеха. То, что вы помещаете в тело сторожа, — это код, который будет выполняться при сбое, что должно привести к передаче управления (return, throw и т. д.). Поэтому код успеха должен идти сразу после охранника. Кроме того, переключение true не имеет смысла, конечно, переключение index имело бы больше смысла, поскольку это то, что вы сравниваете? И что такое T? Ваша функция не является универсальной, это универсальный класс?   -  person Hamish    schedule 25.05.2016
comment
Когда я попытался переключить индекс, я столкнулся с шаблоном выражения сообщения об ошибке типа «Bool», который не может соответствовать значениям типа «Int». Класс Linked List является универсальным — при создании экземпляра я указываю, каким типом объекта должно быть значение каждого узла.   -  person karan satia    schedule 25.05.2016
comment
Вам нужно использовать возможные значения вашего index в качестве случаев, то есть case 0, case count-1.   -  person Hamish    schedule 26.05.2016
comment
Вы должны принять ответ CharlesA, но несколько несвязанных наблюдений: во-первых, getValue, похоже, извлекает элемент из связанного списка. Вместо того, чтобы исправлять имя в соответствии со стандартами (как это сделал CharlesA, переименовав его в getValueAtIndex), я бы просто сделал его subscript. Во-вторых, switch true {...} избыточен. На самом деле я бы потерял операторы if и использовал значимые значения case в switch index { ... }. В-третьих, в вашем коде есть пути, по которым вы не возвращаете значение. Например, см. gist.github.com/robertmryan/4286f243169b1394f0f31d4659a03e5b.   -  person Rob    schedule 26.05.2016


Ответы (2)


Оператор guard используется для обнаружения недопустимого регистра, поэтому вам нужно что-то вроде:

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
}
person Charles A.    schedule 25.05.2016
comment
Обычный return здесь не скомпилируется, потому что функция имеет возвращаемый тип (не пустой). - person Martin R; 25.05.2016
comment
@MartinR Хороший вопрос, переключил его на fatalError и добавил примечание. - person Charles A.; 25.05.2016

Оператор guard предназначен для перемещения программы из ее текущей области или для вызова функции noreturn, если обнаружено, что значение равно nil. Однако вы выполняете весь оператор switch в своем guard.

Согласно Документация Apple Guard:

Предложение else в операторе guard является обязательным и должно либо вызывать функцию, отмеченную атрибутом noreturn, либо передавать управление программой за пределы области, охватывающей оператор Guard, с помощью одного из следующих операторов:

  • возвращаться
  • ломать
  • Продолжать
  • бросать

Хорошим примером guard может быть:

var optValue : String?

guard let optValue = optValue else {return}
person ZGski    schedule 25.05.2016