web-dev-qa-db-ja.com

Never returnタイプとは何ですか?

戻り値の型がfuncNeverは何をしますか?

例えば:

func addNums() -> Never {

    //my code

}

このようにVoidとして戻り型を保持した場合の違いは何ですか?

func addNums() -> Void {

    //my code

}

fatalErrordpassage で述べたように)を処理したいとします。以下のコードで十分です。

print("its an error")
return

Appleのドキュメントによると:

正常に戻らない関数の戻りタイプ、つまり値のないタイプ。

ソース: Developer

これは Swiftで@noreturn属性を使用する方法と方法 の重複した質問ではありませんでした。

  1. 戻り型としてのNeverVoidの両方の違いに関する実際的な例

  2. これらの戻り型を採用する条件。

  3. また、戻り値の型がnilになる可能性があります。その機能の比較も必要です

答えは違いに焦点を合わせるべきです。

25
Saranjith

Never戻り型がSwift 3で導入され、_@noreturn_キー。

この提案の正当化を参照してください。
SE-0102 @noreturn属性を削除し、空のNever型を導入します

公式文書が説明しているように:

正常に戻らない関数の戻りタイプ。値のない型。

無条件にエラー、トラップ、またはその他の方法で終了しないクロージャー、関数、またはメソッドを宣言するときは、戻り値の型としてNeverを使用します。

ソース: https://developer.Apple.com/documentation/Swift/never

基本図:

_// The following function is our custom function we would use
// to manually and purposefully trigger crash. In the logs,
// we can specify what exactly went wrong: e.g. couldn't cast something, 
// couldn't call something or some value doesn't exist:
func crashApp() -> Never {
    fatalError("Something very, very bad happened! Crash the app!")
}
_

Erica Sadun で参照される、_@noreturn_に対する使用方法と利点

  • 関数またはメソッドがスローすることを許可しません。 ()throws-> Never。スローすると、返されることが予期されていなかった関数であっても、エラー修復のためのセカンダリパスが許可されます。
  • 最初のクラス型として、Neverは@noreturn属性ができない方法でジェネリックを操作します。
  • 関数が戻り型と戻りなしの両方を同時に要求することを予防的に防止することはありません。これは、古いシステムでは潜在的な問題でした。

最初の注意事項(二次エラーの修正に関して)は、おそらく特に重要です。 Never関数は複雑なロジックとスローを持つことができます-必ずしもクラッシュするわけではありません。

いくつかの興味深い使用例とNeverVoidの比較を見てみましょう

決して

例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

これらの例では、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を使用して、コンパイラーに呼び出し元サイトに戻らないことを通知します。アプリケーションの実行ループは終了します。

26
Hexfire

Void

Void自体は戻り値の型であり、要素がゼロのタプルです。 Voidと()は同じ意味で使用できます。

これらの例を見てください、

  1. func yourFunc() {}これは基本的に要素がゼロのタプルを返す戻り型のない関数で、()と書くことができます

  2. func yourFunc() -> Void {}voidの戻り値の型についてコンパイラに明示的に通知する関数

  3. func yourFunc() -> () {}()のこの戻り型は、void型と同じように表示されます。 ()は、要素がゼロのタプルを示します

なし

Return-typeは、空のTuple()を返す必要がないことをコンパイラに通知しません。また、クラッシュ、致命的エラー、中止、終了など、現在の実行の終了ポイントには、戻り値のないタイプの関数が使用されます。

neverの詳細を理解するために、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 )。コンパイラは警告を生成しません。

abort()

public func abort() -> Never

同様にexit():

public func exit(_: Int32) -> Never

Apple documentation says: "無条件にエラー、トラップ、その他の方法で終了しないクロージャ、関数、またはメソッドを宣言するとき、戻り値の型としてNeverを使用する。

そのため、壊滅的なエラーを記録するカスタム関数を作成したい場合は、戻り型Neverを使用してコンパイラに通知する必要があります:

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

要するに、「回復が不可能な突然の全体的な障害には決して使用しないでください。

7
Nikunj Joshi

Neverは、関数が戻らないことを示します。多くの場合、エラーを記録した後にプログラムを意図的にクラッシュさせるfatalErrorのようなものに使用することを目的としています。アプリケーションで致命的なエラーのハンドラーを作成するようなことをしていない限り、おそらく使用すべきではありません。

これは、2番目のスニペットのように、値を返さない関数とは異なります。 func addNums() -> Voidと書くこともできます。

4
dpassage

NeverVoidをよく理解し、_Neverが古い_@noreturn_よりも多くのコンテキストでどのように役立つかを理解するために、まず2つのタイプが実際に何であるかを見てみましょう定義:


Neverは次のように定義されます ここ

_public enum Never {}
_

空の列挙型の値をインスタンス化する方法がないため、型システムはNeverのインスタンスが存在できないことを保証します。つまり、戻り値の型をNeverとして指定する関数は、どのような状況でも型システムによって実際に戻ることができません。

コンパイラーは、制御フロー分析を行うときにこれを考慮します。たとえば、これら2つの関数は両方ともエラーなしでコンパイルされますが、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 = ()
_

空のタプルの2つの異なるインスタンスはありません。したがって、Voidを返す関数の戻り値には情報がありません。

実際にreturn ()またはreturn Void()と書くことができます。次のように、返される「値」を使用することもできます。

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

ただし、コンパイラは「定数「v」のタイプが「Void」であると推測されますが、これは予期しない可能性があります」と警告します。


NeverVoidの両方を特殊な言語機能としてではなく、型システムの観点から定義することで、ジェネリックを使用してかなり巧妙なことができます。成功タイプと失敗タイプの両方を包括するResultタイプの例を見てみましょう。

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

これの可能な特殊化は_Result<Void, MyError>_です。これは、成功すると、成功したという事実以外の情報を保持しないという結果になることを意味します。

別の可能性は_Result<String, Never>_である可能性があります。この結果は、コンパイラーによってエラーが発生しないことが保証されています。

オプションは、NeverおよびVoidと同様の方法で対話します。 _Never?_はnilのみであり、_Void?_はnilであるかどうかに関係なく、それ以上の情報は保持しません(基本的にはより複雑なBoolです)。これらの両方は、単独ではあまり有用ではありませんが、NeverまたはVoidがどこかで汎用パラメーターとして使用される場合に表示されることがあります。


実際には、Neverを返す関数を記述することはほとんどありません。 fatalErrorをラップするために個人的に使用して、まだ実装されていない関数をマークするために使用する関数を作成しました。

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

Neverを返す関数の別の例はdispatchMain()です。これは、コマンドラインユーティリティで_DispatchQueue.main_を開始するために使用できます。このキューは新しいブロックを待機するため、dispatchMain()は戻りません。

3
Ahti