2014-10-26 4 views
10

Ich möchte eine int -Rückgabe-Funktion schreiben, die eine Schließung akzeptiert Null Argumente, eine Schließung ein Argument, und eine Schließung nehmen zwei Argumente, wo alle Abschluss Argumente sind Geben Sie int ein und jeder Abschluss gibt f32 zurück.Verwenden von Fn Traits/Closures in Signaturen in Rust

Wie sieht die Signatur dieser Funktion aus?

Nun möchte ich über die Fn und FnMut Merkmale akzeptieren. Wie sieht die Signatur aus? Ist die Verwendung von Features in der Kiste erforderlich? Wenn ja, welche und warum?

Wenn bekannt: Wie sieht es gezuckert aus? Entzuckert?

Wenn bekannt: Was wird sich in Zukunft wahrscheinlich ändern?

Antwort

9

Ich mag eine int-Rückkehr-Funktion schreiben, die eine Schließung Null Argumente nehmen akzeptiert, eine Schließung ein Argument nehmen, und einen Verschluss zwei Argumente nehmen, in dem alle Schließung Argumente vom Typ int sind und jeder Verschluss gibt f32 zurück.

Wie sieht die Signatur dieser Funktion aus?

Funktion Signatur und deren Nutzung zur Zeit (2014.10.26 nächtlichen) könnte wie folgt aussehen:

#![feature(unboxed_closures, unboxed_closure_sugar, overloaded_calls)] 

fn closures<F1, F2, F3>(mut f1: F1, mut f2: F2, mut f3: F3) -> int 
    where F1: FnMut() -> f32, 
      F2: FnMut(int) -> f32, 
      F3: FnMut(int, int) -> f32 { 
    (f1() + f2(10) + f3(20, 30)) as int 
} 

fn main() { 
    let x = closures(
     |&mut:| 0.1, 
     |&mut: x: int| (2*x) as f32, 
     |&mut: x: int, y: int| (x + y) as f32 
    ); 
    println!("{}", x); 
} 

können Sie verwenden Fn statt FnMut (und entfernen mut vor f1, f2 und f3) Wenn Sie den Anrufer zwingen wollen, Schließungen zu passieren, die nicht ihre Umgebung verändern, aber im Allgemeinen, denke ich, würden Sie FnMut verwenden wollen.

Dieser Code verwendet ungeschachtelte Schließung Zucker und überlastete Anrufe. Ohne sie würde es so aussehen:

#![feature(unboxed_closures)] 

fn closures<F1, F2, F3>(mut f1: F1, mut f2: F2, mut f3: F3) -> int 
    where F1: FnMut<(), f32>, 
      F2: FnMut<(int,), f32>, 
      F3: FnMut<(int, int), f32> { 
    (f1.call_mut(()) + f2.call_mut((10,)) + f3.call_mut((20, 30))) as int 
} 

fn main() { 
    let x = closures(
     |&mut:| 0.1, 
     |&mut: x: int| (2*x) as f32, 
     |&mut: x: int, y: int| (x + y) as f32 
    ); 
    println!("{}", x); 
} 

Der Zucker wird verwendet Verschlussart Syntax prettify und überlastete Anrufe Funktion ermöglicht explizite call_* Methoden zu verzichten.

Als für das, was in Zukunft ändern wird, dann ist es wahrscheinlich, dass Syntax Verschlusskonstruktion vereinfacht werden würde (wenn Strom Verschlüsse werden gelöscht), so dass die main() Bit wird wie folgt aussehen:

fn main() { 
    let x = closures(
     || 0.1, 
     |x| (2*x) as f32, 
     |x, y| (x + y) as f32 
    ); 
    println!("{}", x); 
} 

die tatsächliche Art des Verschlusses (FnMut, Fn oder FnOnce) wird abgeleitet.

Es gibt auch andere Änderungen, wie move Schlüsselwort für Schließungen, die von Funktionen zurückgegeben werden (move wirkt sich auf die Variablen Erfassung Semantik). Dies wird durch this akzeptiert RFC abgedeckt.

Im Allgemeinen sind ungeordnete Schließungen in this RFC beschrieben. Es wird jedoch nicht aktualisiert, wenn die Zucker-Syntax für neue Verschlüsse und andere subtile Änderungen verwendet werden. Es kann besser sein, Rust issue tracker zu folgen, um mehr darüber herauszufinden. Zum Beispiel werden viele Probleme mit nicht abgeschlossenen Schließungen in this Bug zusammengefasst.

+0

Huh. Ich habe die RFCs durchsucht und versucht, Informationen über die Fn-Merkmale zu finden, und ich denke, ich habe es vermisst, weil ich nach "Merkmalen" und nicht nach "Schließungen" suchte. Vielen Dank! – user

+0

Ja, diese Dinge sind ungeöffnete Schließungen, Sie werden eine Menge auf ihnen finden, wenn Sie nach diesem Begriff suchen. –

4

Fn, FnMut und FnOnce sind die drei Merkmalstypen, die mit ungeöffneten Verschlüssen eingeführt wurden. Der Unterschied zwischen diesen Eigenschaften, neben dem Namen ihrer einzigen Methode, ist, dass die self Parameter zu diesen Methoden anders übergeben wird:

  • Fn: &self (Bezug genommen wird, kann nicht die Schließung der Umgebung mutieren)
  • FnMut: &mut self (Bezug genommen wird, kann die Umgebung des Verschlusses mutieren)
  • FnOnce: self (nach Wert, den Verschluss verbraucht, so kann der Verschluss nur einmal aufgerufen werden)

Diese Merkmale haben zwei Typparameter: Args, ein Tupeltyp, der die Parameter des Verschlusses darstellt (oder (), wenn der Verschluss keine Parameter annimmt) und Result, der Rückgabetyp des Verschlusses.

nun Ihre Frage zu beantworten:

  • Closure unter Null Argumente:

    fn foo<F: Fn<(), f32>>(closure: F) -> int { 
        0 
    } 
    
    • Alternativ kann das gebundene geschrieben werden unter Verwendung einer where Klausel:

      fn foo<F>(closure: F) -> int where F: Fn<(), f32> { 
          0 
      } 
      
  • Closure nimmt ein Argument:

    fn foo<F: Fn<(int), f32>>(closure: F) -> int { 
        0 
    } 
    
  • Closure nimmt zwei Argumente:

    fn foo<F: Fn<(int, int), f32>>(closure: F) -> int { 
        0 
    } 
    
  • Closure unter Null Argumente, gebrannte Form:

    fn foo<F: Fn() -> f32>(closure: F) -> int { 
        0 
    } 
    
  • Closure ein Argument , gezuckerte Form:

    fn foo<F: Fn(int) -> f32>(closure: F) -> int { 
        0 
    } 
    
  • Closure nehmen zwei Argumente, gezuckerte Form:

    fn foo<F: Fn(int, int) -> f32>(closure: F) -> int { 
        0 
    } 
    

Die alte "boxed" Verschlüsse werden entfernt. Sie können Fehler auf nicht verschlossenen Schließungen auf der metabug verfolgen.