2016-01-15 11 views
9

Unterstützt Rust Verschlüsse mit generischen Rückgabetypen? Zum Beispiel möchte ich etwas in der Art schreiben:Mögliche generische Schließung definieren?

let get<T: FromValue> = |s: &str| -> Option<T> { ... } 

Aber diese Syntax ist eindeutig falsch.

Was ich versuche zu tun

ich mit rust-mysql-simple arbeiten bin, und ich bin eine from_row Methode für meine User struct schreiben, einen Benutzer aus einer Datenbankzeile zu bauen.

Die Bibliothek bietet (so weit ich das beurteilen kann) keine Möglichkeit, Abfrageergebniszeilen nach Spaltennamen zu suchen. Also, dies zu umgehen, meine Methode sieht so aus (das kompiliert und funktioniert einwandfrei):

fn from_row(result: &QueryResult, row: Vec<Value>) -> User { 

    let mut map: HashMap<_, _> = row.into_iter().enumerate().collect(); 

    let mut get = |s: &str| { 
     result.column_index(s) 
      .and_then(|i| map.remove(&i)) 
    }; 

    User { 
     id: get("id").and_then(|x| from_value_opt(x).ok()) 
    } 
} 

Hier result ist ein Objekt, die Informationen über die Anfrage des Spaltennamen enthält (verwendet, um den Spaltenindex für einen Spaltennamen zu finden) und die row enthält die geordneten Werte aus einer Abfrageergebniszeile. from_value_opt ist eine von der Bibliothek bereitgestellte Methode, die eine Value übernimmt und eine Result<T, MyError> zurückgibt. Der Wert wird auf den Feldtyp angewendet.

Ich habe versucht, die .and_then(|x| from_value_opt(x).ok()) in die get Schließung zu verschieben, nur um den Code etwas aufzuräumen. In diesem Fall wird der Rückgabetyp der Schließung als Ergebnis des ersten Auftretens des Aufrufs get interpretiert.

ich die Schließung als verschachtelte Verfahren neu geschrieben, die wie folgt aussieht: viel

fn get<T: FromValue>(r: &QueryResult, m: &mut HashMap<usize, Value>, s: &str) 
    -> Option<T> { ... } 

das auch gut funktioniert, aber nicht helfen, die Ausführlichkeit zu schneiden.

+0

Dank, habe ich einige Informationen über das, was ich zu tun versuchen. –

Antwort

3

Nein, AFAIK können Sie nicht. Ich meine, Sie können definieren eine generische Schließung, was Sie nicht tun können, ist eine Bindung mit einer generischen linken Seite zu schaffen.

A fn get<T>, als die, die Sie erwähnen Umschreiben, erfährt monomorphisation, das heißt, wenn es kompilieren, erzeugt rustc eine andere Version von get für jeden tatsächlichen T, die es nennen verwendet wird. Zu dem Zeitpunkt, zu dem Sie das Ergebnis dieser get (let a = get(...)) zuordnen, hat das Ergebnis eine konkrete Art und Größe.

Eine let Bindung wird nicht monomorphisiert, Sie können also keine let a<T> = ... haben und haben eine andere Version von a, die vom Compiler für Sie generiert wurde.

Was ich denke, könnte dies ermöglichen die Einführung von höher-kinded Typen, die eine der heißbegehrten, aber noch nicht vollständig ausgearbeiteten neuen Funktionen für Rust ist. Sie würden es Ihnen ermöglichen, so etwas wie zu schreiben:

// does not work as of Rust 1 
let a = for<T> |s: &str, t: T| {...} 

das heißt Rückkehr einen Verschluss, die ich mit einem T später parametrisieren kann (das ist, was Sie fordern).

+0

Danke! Das macht vollkommen Sinn. –

+0

@kardeiz Nicht wissen, wie Benutzer definiert ist und was von_value_opt zurückgibt, ist schwer zu sagen, aber es ist möglich, dass Sie nicht unbedingt generisch sein müssen und Sie können dem Compiler helfen, indem Sie auf den Rückgabetyp wie from_value_opt :: (x) –

+0

Ja, das funktioniert, aber ich glaube nicht, dass mir das weiterhelfen wird, da meine 'User'-Felder unterschiedliche Typen haben. Ich habe gerade versucht, den 'from_value_opt' Aufruf in die Closure zu setzen und die Closure wie' get :: > ("id") aufzurufen, aber anscheinend kann ich keine Typparameter an eine Closure übergeben. –

1

Der Schließungstyp ist anonym, so dass Sie nicht in der Lage sein werden, es aufzuschreiben, und es scheint, dass der Compiler nicht in der Lage ist, daraus zu schließen, also kein Glück.

Aber gibt es einen bestimmten Grund, einen Verschluss zu verwenden? Wenn ich Ihre Frage richtig verstanden habe, verwenden Sie einfach diese Funktion, um einige sich wiederholende Aktionen herauszufiltern, und Sie werden sie nicht wirklich weitergeben. Ein interner fn sollte also gut funktionieren. Der Nachteil ist, dass Sie alle Werte übergeben müssen, die bei Ihrer Schließung automatisch erfasst wurden.

Es wäre so ähnlich sein (das Beispiel immer noch ziemlich komplex ist, so versuche ich es nicht zu kompilieren):

fn from_row(result: &QueryResult, row: Vec<Value>) -> User { 
    let mut map: HashMap<_, _> = row.into_iter().enumerate().collect(); 

    fn get<T: FromValue>(s: &str, result: &QueryResult, map: &mut HashMap<_, _>) 
     -> T { 
     result.column_index(s) 
      .and_then(|i| map.remove(&i)) 
      .and_then(|x| from_value_opt(x)).ok() 
    }; 

    User { 
     id: get("id", &result, &mut map) 
    } 
} 
+0

Danke für die Antwort. Ja, deshalb wollte ich eine Schließung machen - also müsste ich diese Werte nicht weitergeben. Dies ist jedoch die Art und Weise, wie ich gelandet bin (siehe den letzten Teil meiner Frage). –