戻り値の型がfunc
のNever
は何をしますか?
例えば:
func addNums() -> Never {
//my code
}
このようにVoid
として戻り型を保持した場合の違いは何ですか?
func addNums() -> Void {
//my code
}
fatalError
( dpassage で述べたように)を処理したいとします。以下のコードで十分です。
print("its an error")
return
Appleのドキュメントによると:
正常に戻らない関数の戻りタイプ、つまり値のないタイプ。
ソース: Developer
これは Swiftで@noreturn属性を使用する方法と方法 の重複した質問ではありませんでした。
戻り型としてのNever
とVoid
の両方の違いに関する実際的な例
これらの戻り型を採用する条件。
また、戻り値の型がnilになる可能性があります。その機能の比較も必要です
答えは違いに焦点を合わせるべきです。
Never
戻り型がSwift 3で導入され、_@noreturn
_キー。
この提案の正当化を参照してください。
SE-0102 @noreturn属性を削除し、空のNever型を導入します
公式文書が説明しているように:
正常に戻らない関数の戻りタイプ。値のない型。
無条件にエラー、トラップ、またはその他の方法で終了しないクロージャー、関数、またはメソッドを宣言するときは、戻り値の型として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
_に対する使用方法と利点
最初の注意事項(二次エラーの修正に関して)は、おそらく特に重要です。 Never
関数は複雑なロジックとスローを持つことができます-必ずしもクラッシュするわけではありません。
いくつかの興味深い使用例とNever
とVoid
の比較を見てみましょう
例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
を使用して、コンパイラーに呼び出し元サイトに戻らないことを通知します。アプリケーションの実行ループは終了します。
Void
Void自体は戻り値の型であり、要素がゼロのタプルです。 Voidと()は同じ意味で使用できます。
これらの例を見てください、
func yourFunc() {}
これは基本的に要素がゼロのタプルを返す戻り型のない関数で、()と書くことができます
func yourFunc() -> Void {}
voidの戻り値の型についてコンパイラに明示的に通知する関数
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)
}
要するに、「回復が不可能な突然の全体的な障害には決して使用しないでください。」
Never
は、関数が戻らないことを示します。多くの場合、エラーを記録した後にプログラムを意図的にクラッシュさせるfatalError
のようなものに使用することを目的としています。アプリケーションで致命的なエラーのハンドラーを作成するようなことをしていない限り、おそらく使用すべきではありません。
これは、2番目のスニペットのように、値を返さない関数とは異なります。 func addNums() -> Void
と書くこともできます。
Never
とVoid
をよく理解し、_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」であると推測されますが、これは予期しない可能性があります」と警告します。
Never
とVoid
の両方を特殊な言語機能としてではなく、型システムの観点から定義することで、ジェネリックを使用してかなり巧妙なことができます。成功タイプと失敗タイプの両方を包括する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()
は戻りません。