Что никогда не возвращает тип - функции Swift?

Что делает func с типом возврата Никогда?

Например:

func addNums() -> Never {

//my code

}

Какая разница, если я сохранил тип возвращаемого значения следующим образом?

func addNums() -> Void {

//my code

}

Изменить

Предположим, что я хочу обработать fatalError, как указано dpassage, ниже кода будет достаточно.

print("its an error")
return

Объяснение Apple по типу Никогда не используется

Возвращаемый тип функций, которые явно не указывают тип возврата

Это был не дублированный вопрос из SO Post, я хочу узнать более подробные ответы на такие детали, как:

  • Практические примеры различий между типами возврата как Never, так и Void

  • Средство, в котором мы принимаем возвращаемые типы. Также есть вероятность, что тип возврата может быть равен нулю. Нужно также сравнить эту функцию.

Ответ должен быть сосредоточен на различиях.

Ответы

Ответ 1

Этот был введен в Swift 3 для замены клавиши @noreturn.

См. оправдание в этом предложении:  SE-0102 Удалить атрибут @noreturn и ввести пустой Неверный тип

Как поясняется официальная документация:

Возвращаемый тип функций, которые не возвращаются нормально; тип с нет значений.

Использовать Never как возвращаемый тип при объявлении закрытия, функции или метода, который безошибочно выдает ошибку, ловушки или иначе не завершается.

Источник: https://developer.apple.com/documentation/swift/never

func crashAndBurn() -> Never {
    fatalError("Something very, very bad happened")
}

Спецификации использования и преимущества по сравнению с @noreturn, как указано Эрика Садун:

  • Никогда не позволяет бросить функцию или метод: например,() бросает → Никогда. Throwing позволяет вторичный путь для исправления ошибок даже в тех функциях, которые не ожидали возврата.
  • Как тип первого класса, никогда не работает с дженериками так, что атрибут @noreturn не мог.
  • Никогда не проактивно запрещает функции требовать как возвращаемый тип, так и невозврат в одно и то же время. Это была потенциальная проблема в старой системе.

Практическое использование в конечном итоге сводится к удобству разработчика/компилятора:

Никогда

Пример 1

func noReturn() -> Never {
    fatalError() // fatalError also returns Never, so no need to `return`
}

func pickPositiveNumber(below limit: Int) -> Int {
    guard limit >= 1 else {
        noReturn()
        // No need to exit guarded scope after noReturn
    }
    return rand(limit)
}

Пример 2

func foo() {
    abort()
    print("Should not reach here") // Warning for this line
}

Пример 3

func bar() -> Int {
    if true {
        abort() // No warning and no compiler error, because abort() terminates it.
    } else {
        return 1
    }
}

abort() определяется как:

public func abort() -> Never

Недействительные

Эти примеры не были бы возможны, если бы он возвращал Void:

public func abortVoid() -> Void {
    fatalError()
}

func bar() -> Int {
    if true {
        abortVoid() // ERROR: Missing return in a function expected to return 'Int'
    } else {
        return 1
    }
}

И чтобы упаковать его с помощью abort(), возвращающего Never:

func bar() -> Int {
    if true {
        abort() // No ERROR, but compiler sees it returns Never and warns:
        return 2 // Will never be executed
    } else {
        return 1
    }
}

Мы используем Void, чтобы сообщить компилятору, что нет возвращаемого значения. Приложение продолжает работать.

Мы используем Never, чтобы сообщить компилятору, что нет возврата на сайт вызывающего абонента. Запуск приложения runloop завершен.

Ответ 2

Never указывает, что функция никогда не вернется. Он предназначен для использования для таких вещей, как fatalError, которые заставляют вашу программу рушиться преднамеренно, часто после регистрации ошибки. Вероятно, вам не следует использовать его, если вы не делаете что-то вроде создания обработчика катастрофических ошибок в вашем приложении.

Это отличается от функции, которая просто не возвращает значение, как во втором фрагменте. Вы также можете написать это как func addNums() -> Void.

Ответ 3

Пустота

Void сам по себе является возвращаемым типом, который заполняется нулевым элементом. Вы можете использовать Void и() взаимозаменяемые.

Посмотрите на эти примеры,

  • func yourFunc() {} Это функция без возвращаемого типа, которая в основном возвращает tupple с нулевым элементом, который может быть записан как()

  • func yourFunc() -> Void {} Функция, которая явно информирует компилятор о типе возврата void

  • func yourFunc() -> () {} Этот возвращаемый тип() отображается так же, как и тип void.() указывает на тупп с нулевым элементом

Никогда

Никогда не возвращай-тип сообщает компилятор, что даже нет необходимости возвращать пустой кортеж() также, поскольку функция с типом никогда не возвращается для точки выхода для текущего выполнения, например, сбой, фатальная ошибка, прерывание или выход.

Для детального понимания никогда, давайте рассмотрим пример abort():

1.

 func yourFunc() {
    abort()
    print("Will not reach at this point") //Warning for this line
} 

2.

 func yourFunc() -> Int {
    if true {
        abort()
    } else {
        return 1
    }
}

Из приведенных выше фрагментов кода мы можем видеть , когда мы вызываем abort() (который не возвращает значение) в качестве последнего оператора в функции, которая ожидает, что значение будет возвращено (в нашем случае Int) компилятор не генерирует предупреждение.

прервать()

public func abort() -> Never

Аналогично для exit():

public func exit(_: Int32) -> Never

В документации на Apple написано: " Использовать Never как возвращаемый тип при объявлении закрытия, функции или метода, который безошибочно выдает ошибку, ловушки или иным образом не завершается."

Итак, если вы хотите написать настраиваемую функцию, которая регистрирует катастрофическую ошибку, вы должны использовать возвращаемый тип Never, чтобы сигнализировать компилятору:

func catastrophicErrorDisplay(error: String) -> Never {
    DisplaySomeCustomLogFacility(error)
}

Короче говоря, Никогда не используется для внезапного и полного отказа, из которого невозможно восстановить. "

Ответ 4

Чтобы лучше понять Never и Void, и как Never полезен в большем количестве контекстов, чем старый @noreturn, давайте сначала посмотрим, какие два типа на самом деле определены как:


Never определяется здесь как:

public enum Never {}

Поскольку невозможно создать экземпляр пустого перечисления, система типов гарантирует, что экземпляр Never не может существовать. Это означает, что функции, определяющие их тип возвращаемого типа, как Never предотвращаются системой фактического возврата при любых обстоятельствах.

Компилятор учитывает это при анализе потока управления. Например, эти две функции компилируются без ошибок, тогда как они не сработают, если функция, возвращающая Void, была заменена на fatalError:

func foo(fail: Bool) -> String {
    if fail {
        fatalError()
    } else {
        return "foo"
    }
    // notice there is no return statement here
}

func bar(fail: Bool) -> Void {
    let s: String
    if fail {
        fatalError()
        // the compiler doesn't complain s is not initialized here
    } else {
        s = "bar"
    }
    print(s)
}

Void определяется здесь как:

public typealias Void = ()

Нет двух разных экземпляров пустого кортежа. Таким образом, возвращаемое значение возвращаемых функций Void не содержит информации.

Фактически вы можете написать return () или return Void(). Вы также можете использовать возвращаемое значение "значение":

func empty() -> Void {}
let v = empty()
print(type(of: v)) // prints "()"

хотя компилятор будет предупреждать "Constant" v ', чтобы иметь тип "Void", что может быть неожиданным ".


Определение как Never, так и Void в терминах системы типов, а не как специальные языковые функции позволяет нам делать некоторые довольно умные вещи с помощью дженериков. Давайте рассмотрим пример типа Result, общий для обоих типов успеха и отказа.

enum Result<R, E> {
    case success(R)
    case failure(E)
}

Возможной специализацией этого будет Result<Void, MyError>. Это означало бы, что у вас есть результат, который при успехе не содержит никакой информации, кроме того, что это удалось.

Другой возможностью может быть Result<String, Never>. Этот результат гарантируется компилятором, чтобы никогда не быть случаем сбоя.

Опционы взаимодействуют с Never и Void аналогичным образом. Never? может быть только когда-либо равным нулю, а Void? содержит только информацию, которая равна нулю или нет, не более (это в основном более сложный Bool). Оба они не очень полезны сами по себе, но могут появляться, когда Never или Void используются как общие параметры где-то.


На практике вы редко будете писать функции, возвращающие Never. Я лично использовал его для переноса fatalError, чтобы создать функцию, которую я использую, чтобы отметить еще не реализованные функции:

func unimplemented(f: String = #function) -> Never {
    fatalError("\(f) is not implemented yet")
}

Другим примером функции, возвращающей Never, является dispatchMain(), который можно использовать в утилитах командной строки для запуска DispatchQueue.main. Поскольку эта очередь ждет новых блоков, dispatchMain() никогда не возвращается.