2016-12-05 15 views
5

Próbuję nauczyć się Rusta, i jak wielu przedtem wyruszyłem, aby napisać iterator sekwencji Fibonacciego do ćwiczeń. Mój pierwszy przebieg używał u32 s i działał dobrze, więc postanowiłem spróbować napisać ogólną wersję. To jest mój wynik:Jak uniknąć nadmiernego klonowania w Rust?

use num::Integer; 
use std::ops::Add; 

pub struct Fibonacci<T: Integer + Add + Clone> { 
    nth: T, 
    n_plus_one_th: T, 
} 

impl<T: Integer + Add + Clone> Iterator for Fibonacci<T> { 
    type Item = T; 
    fn next(&mut self) -> Option<T> { 
     let temp = self.nth.clone(); 
     self.nth = self.n_plus_one_th.clone(); 
     self.n_plus_one_th = temp.clone() + self.n_plus_one_th.clone(); 
     Some(temp) 
    } 
} 

impl<T: Integer + Add + Clone> Fibonacci<T> { 
    pub fn new() -> Fibonacci<T> { 
     Fibonacci { 
      nth: T::one(), 
      n_plus_one_th: T::one(), 
     } 
    } 
} 

testowałem to z u32 i num::BigUint, i to działa dobrze. Martwię się jednak o wszystkie klonowania w metodzie . W szczególności nie rozumiem, dlaczego potrzebuję sklonować podczas kroku dodawania.

Podejrzewam, że istnieje lepszy sposób napisania tego przy użyciu bardziej zaawansowanych pojęć odniesienia Rusta, ale jak dotąd nie odkryłem tego.

Odpowiedz

7

Rozwiązaniem jest użycie where klauzuli tak:

extern crate num; 

use num::One; 
use std::ops::Add; 

pub struct Fibonacci<T> { 
    nth: T, 
    n_plus_one_th: T, 
} 

impl<T> Fibonacci<T> 
    where T: One 
{ 
    pub fn new() -> Fibonacci<T> { 
     Fibonacci { 
      nth: T::one(), 
      n_plus_one_th: T::one(), 
     } 
    } 
} 

impl<T> Iterator for Fibonacci<T> 
    where for<'a> &'a T: Add<&'a T, Output = T> 
{ 
    type Item = T; 
    fn next(&mut self) -> Option<T> { 
     use std::mem::swap; 
     let mut temp = &self.nth + &self.n_plus_one_th; 
     swap(&mut self.nth, &mut temp); 
     swap(&mut self.n_plus_one_th, &mut self.nth); 
     Some(temp) 
    } 
} 

szczególności klauzula for<'a> &'a T: Add<&'a T, Output=T> brzmi „dla każdego życia 'a, &'a T musi wdrożyć Add z RHS z &'a T i Output=T Oznacza to, że. można dodać dwa &T s, aby uzyskać nowy T.

Z tym, jedynym pozostałym problemem jest tasowanie wartości wokół, które można wykonać za pomocą swap.

Wziąłem również wolność od ograniczeń uproszczenie gdzie indziej (tylko potrzebne One, nie Integer).

+0

Jedna szybka odpowiedź: Zauważyłem, że nie wprowadziłeś żadnych ograniczeń w samej strukturze, tylko w implementacjach. Czy to jest konwencja Rusta? –

+0

@MarkTozzi Zwykle zależy to od osobistych preferencji. Możesz wszędzie powtórzyć granice, jeśli chcesz utrudnić "niewłaściwe użycie" typu, ale na przykład wydawało się to przesadzone. –

+0

W przypadku, gdy ktoś się zastanawia: konstrukcja "gdzie dla" jest nazywana (The Magic of) cechami wyższej rangi (HRTB) i jest udokumentowana tutaj https://doc.rust-lang.org/nomicon/hrtb. html – Sebastian