割り当て後に汎用パラメーター「T」を推測できませんでした

Aug 18 2020
// Xcode 11.6 / Swift 5

import Foundation

func f<T>(_: (T) -> Void) { }

@discardableResult
func g(_: Int) -> Int { 0 }

f { g($0) } // compiles fine f { let _ = g($0) }     // Generic parameter 'T' could not be inferred

上記のコードでは、汎用関数fはその引数として型の引数を取る関数を期待していますT

この関数gは、型の引数を取りますInt

を書くf { g($0) }と、コードがコンパイルされます。私は信じているコンパイラはそれを推測することができますので、このコンパイルを(私が間違っている場合は、私を修正してください)TですInt基づいてgの引数の型。

ただし、gたとえばlet _ = g($0)行で、の戻り値を使用して何かを実行しようとすると、コンパイラは、のタイプを推測できなくなったと文句を言いTます。

の戻り型はg、コンパイラがTの型を推測する方法とは関係がないように思われますが、明らかに関係があります。

なぜこれが起こるのか、そしてそれを修正するために(もしあれば)何ができるのか、誰かが光を当てることができますか?

回答

2 Sweeper Aug 18 2020 at 11:45

これはコンパイラのバグである場合とそうでない場合があります。

SR-1570で述べられているように、Swiftはいくつかのクロージャ、つまりマルチステートメントのクロージャのタイプを推測しようとしないことが知られています。

これは正しい動作です。Swiftは、マルチステートメントクロージャの本体からパラメータを推測したり型を返したりしません。

ただし、クロージャは1つのステートメント、具体的には1つの宣言のみで構成されます。奇妙ではありますが、クロージャーに1つの宣言も含まれている場合に、Swiftが型を推測しようとしないように設計されている可能性があります。たとえば、これもコンパイルされません

f { let x: Int = $0 } // nothing to do with "g"! The issue seems to be with declarations

これが設計によるものである場合、その背後にある理論的根拠は、クロージャ内の単一の宣言がとにかくあまり意味をなさないためである可能性があります。宣言されたものは何でも使用されません。

しかし、繰り返しになりますが、これは単なる推測であり、これもバグである可能性があります。

これを修正するには、単に宣言ではないものにします。

f { _ = g($0) } // this, without the "let", is IMO the idiomatic way to ignore the function result

または

f { g($0) } // g has @discardableResult anyway, so you don't even need the wildcard
2 Malik Aug 18 2020 at 11:44

この関数fは、関数をパラメーターとして受け取ります。パラメーターは、型のパラメーターを受け取り、T何も返しません(Void)。型を自動的に推測するクロージャの場合、単一の(場合によっては単純な)式で構成されている必要があります。複雑なものがあると、コンパイラーが推測するのが難しくなります(これはコンパイラーの観点からは理にかなっています)。どうやら、let _ = g($0)コンパイラに関する限り、これは複雑なステートメントです。詳細については、このメーリングリストのディスカッションを参照してください。

AlexanderNikolaychuk Aug 18 2020 at 11:44

@discardableResultを使用すると、次の2種類の関数を使用できるようになります。

g(_:Int)-> Intおよびg(_:Int)-> Void(関数の結果を使用したくない場合)

私はそう思います

f { g($0) }-ここで、fは同じ型を持っているため、型を推測できます

(_: (T) -> Void) and (_: (Int) -> Void)

f { let _ = g($0) } -この場合、g関数のタイプはf関数とは異なります

(_: (T) -> Void) and (_: (Int) -> Int)

「let」を削除すると、再度コンパイルされます。

f { _ = g($0) }

私はそれを考えるf { let _ = g($0) }-リターンのみのInt値f { _ = g($0) }-リターン機能(_:(INT) - > INT)

多分それはここでの鍵です