2014-10-26 11 views
10

Chcę napisać funkcję zwrócenia, która przyjmuje zamknięcie przyjmujące zero argumentów, zamknięcie przyjmujące jeden argument i zamknięcie przyjmujące dwa argumenty, przy czym wszystkie argumenty zamknięcia są z typ int i każde zamknięcie zwraca f32.Jak używać znaków Fn/zamknięć w podpisach w Rust

Jak będzie wyglądać ta funkcja?

Teraz chcę zaakceptować poprzez cechy Fn i FnMut. Jak wygląda podpis? Czy wymagane jest użycie funkcji w skrzyni? Jeśli tak, to które i dlaczego?

Jeśli jest znana: jak wygląda cukrem? Ścięty?

Jeśli jest znana: co może ulec zmianie w przyszłości?

Odpowiedz

9

Chcę napisać funkcję int-powrocie który akceptuje zamknięcie biorąc zera argumentów, zamknięcie przyjmuje jeden argument i zamknięcie biorąc dwa argumenty, w której wszystkie argumenty zamykające typu int i każde zamknięcie zwraca f32.

Jak będzie wyglądać ta funkcja?

Funkcja Podpis i jego wykorzystanie aktualnie (26.10.2014 nightly) mógłby wyglądać następująco:

#![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); 
} 

Można użyć Fn zamiast FnMut (i usunąć mut przed f1, f2 i f3) jeśli chcesz zmusić dzwoniącego do przekazania zamknięć, które nie zmieniają środowiska, ale ogólnie, myślę, że chciałbyś użyć FnMut.

Ten kod wykorzystuje zamknięty cukier i przeładowane połączenia. Bez nich to będzie wyglądać następująco:

#![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); 
} 

Cukier służy do prettify składni typ zamknięcia, a przeciążone wywołania funkcji pozwala pominąć wyraźne call_* metod.

chodzi o to, co się zmieni w przyszłości, to jest prawdopodobne, że składnia konstrukcja zamknięcia zostaną uproszczone (gdy obecne zamknięcia są odrzucane), więc main() bit będzie wyglądać następująco:

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

Rzeczywisty typ zamknięcia (FnMut, Fn lub FnOnce) zostanie wywnioskowany.

Będą również inne zmiany, takie jak move słowo kluczowe dla zamknięć, które są zwracane z funkcji (move wpływa na semantykę przechwytywania zmiennych). Jest to objęte this zaakceptowanym RFC.

Ogólnie rzecz biorąc, zamknięcia bez opakowania są opisane w dokumencie RFC this. Nie jest on jednak aktualizowany, ponieważ zawiera nową zamkniętą składnię cukru i inne subtelne zmiany; może być lepiej podążać za Rustem issue tracker, aby dowiedzieć się więcej na ten temat. Na przykład wiele problemów związanych z zamknięciami bez opakowania jest zagregowanych w błędzie this.

+0

Huh. Przeglądałem RFC próbując znaleźć informacje na temat cech Fn i myślę, że tęskniłem za tym, ponieważ szukałem "cech", a nie "zamknięć". Dzięki! – user

+0

Tak, te rzeczy nazywa się zamknięciami bez opakowania, znajdziesz na nich wiele, jeśli szukasz tego terminu. –

4

Fn, FnMut i FnOnce to trzy typy cech wprowadzone przy zamkniętych zamknięciach. Różnica pomiędzy tymi cechami, obok nazwy ich jednej metody jest to, że parametr self tych metod jest przekazywane w różny sposób:

  • Fn: &self (odnośnik, nie można mutować warunki zamknięcia'S)
  • FnMut: &mut self (przez odniesienie, może mutować środowiska zamknięcia za)
  • FnOnce: self (pod względem wartości, zużywa zamknięcie, więc zamknięcie może być wywołana tylko raz)

Te cechy mają dwa parametry: Args, czyli typ krotki reprezentujący parametry zamknięcia (lub (), jeśli zamknięcie nie przyjmuje parametrów) i Result, który jest typem powrotu zamknięcia.

teraz odpowiedzieć na pytanie:

  • Zamknięcie przy zerowej argumenty

    fn foo<F: Fn<(), f32>>(closure: F) -> int { 
        0 
    } 
    
    • Alternatywnie, związane mogą być zapisywane za pomocą where klauzuli:

      fn foo<F>(closure: F) -> int where F: Fn<(), f32> { 
          0 
      } 
      
  • Zamknięcie przyjmuje jeden argument:

    fn foo<F: Fn<(int), f32>>(closure: F) -> int { 
        0 
    } 
    
  • zamknięcie biorąc dwa argumenty:

    fn foo<F: Fn<(int, int), f32>>(closure: F) -> int { 
        0 
    } 
    
  • Zamknięcie przy zerowych argumenty, forma słodzone:

    fn foo<F: Fn() -> f32>(closure: F) -> int { 
        0 
    } 
    
  • Zamknięcie biorąc jeden argument , słodzona forma:

    fn foo<F: Fn(int) -> f32>(closure: F) -> int { 
        0 
    } 
    
  • Zamknięcie biorąc dwa argumenty, słodzone forma:

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

Stary "zapakowane" zamknięcia odchodzą. Możesz śledzić błędy w zamkniętych zamknięciach na urządzeniu metabug.