Impossibile dedurre il parametro generico 'T' dopo l'assegnazione

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

Nel codice precedente, la funzione generica fprevede come argomento una funzione che accetta un argomento di tipo T.

La funzione gaccetta un argomento di tipo Int.

Quando scrivo f { g($0) }, il codice viene compilato. Credo (per favore correggimi se sbaglio) questo viene compilato perché il compilatore può dedurre che èT basato Intsul gtipo di argomento di .

Tuttavia, quando provo a fare qualcosa con il valore restituito di g, ad esempio nella let _ = g($0)riga, il compilatore si lamenta di non poter più dedurre il tipo di T.

Mi sembra che il tipo restituito di gnon dovrebbe avere alcuna relazione con il modo in cui il compilatore deduce Til tipo di , ma chiaramente lo fa.

Qualcuno può far luce sul motivo per cui ciò accade e cosa (se non altro) si può fare per correggerlo?

Risposte

2 Sweeper Aug 18 2020 at 11:45

Questo può o non può essere un bug del compilatore.

È noto che Swift non cerca di dedurre i tipi di alcune chiusure, vale a dire quelle con più dichiarazioni, come detto in SR-1570 :

Questo è il comportamento corretto: Swift non deduce i tipi di parametri o restituiti dai corpi delle chiusure con più istruzioni.

Tuttavia, la tua chiusura consiste in una sola dichiarazione, una dichiarazione per essere precisi. È possibile, anche se strano, che l'abbiano progettato in modo tale che Swift non provi a dedurre i tipi se anche la chiusura contiene una dichiarazione . Ad esempio, neanche questo viene compilato

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

Se questo fosse dovuto alla progettazione, la logica alla base potrebbe essere perché una singola dichiarazione in una chiusura non ha molto senso comunque. Qualunque cosa venga dichiarata, non verrà utilizzata.

Ma ancora una volta, questa è solo una speculazione e anche questo potrebbe essere un bug.

Per risolverlo, rendilo semplicemente una non-dichiarazione:

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

O

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

La funzione faccetta una funzione come parametro che a sua volta accetta un parametro di tipo Te non restituisce niente ( Void). Affinché una chiusura inferisca automaticamente i tipi, deve consistere in una singola (e talvolta semplice) espressione. Qualsiasi cosa complessa rende difficile per il compilatore dedurre (il che ha senso dal punto di vista del compilatore). Apparentemente, let _ = g($0)è un'istruzione complessa per quanto riguarda il compilatore. Per ulteriori informazioni, vedere questa discussione sulla mailing list

AlexanderNikolaychuk Aug 18 2020 at 11:44

sembra che @discardableResult ti dia la possibilità di avere 2 tipi di funzioni:

g(_: Int) -> Int e g(_: Int) -> Void (è quando non vuoi usare un risultato della funzione)

penso che

f { g($0) }- qui la tua f può dedurre un tipo perché ha lo stesso tipo

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

f { let _ = g($0) }- in questo caso il tipo di funzione g è diverso dalla funzione f

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

Se rimuoverai "let", verrà compilato di nuovo:

f { _ = g($0) }

Penso che f { let _ = g($0) }- restituisca solo il valore Int f { _ = g($0) }- restituisca la funzione (_: (Int) -> Int)

Forse è una chiave qui