2016-12-05 119 views
5

我想學習Rust,和許多之前我一樣,爲實踐寫出了一個Fibonacci序列迭代器。我的第一個傳球使用了u32 s,並且運行良好,所以我決定嘗試編寫一個通用版本。這是我的結果:如何避免在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(), 
     } 
    } 
} 

我有u32num::BigUint進行了測試,它工作正常。不過,我很關心next方法中的所有克隆。特別是,我不明白爲什麼我需要在添加步驟中進行克隆。

我懷疑有更好的方法來使用一些Rust的更高級的參考概念來編寫它,但到目前爲止我還沒有弄明白。

回答

7

的解決方案是使用一個where子句像這樣:

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) 
    } 
} 

具體地說,for<'a> &'a T: Add<&'a T, Output=T>子句讀作「爲任何壽命'a&'a T必須實現Add&'a TOutput=T一個RHS也就是說,。你可以添加兩個&T s來獲得一個新的T

因此,唯一剩下的問題是混洗值,這可以使用swap

我還冒昧地簡化了別處的限制(您只需要One,而不是Integer)。

+0

快速跟進:我注意到你沒有在結構本身上放置任何特徵邊界,只在實現上。那是一個Rust慣例嗎? –

+0

@MarkTozzi這主要取決於個人喜好。如果你想讓一個類型的「誤用」變得更加困難,那麼你可以在任何地方重複這個界限,但是對於一個例子來說這似乎過分了。 –

+0

如果有人也想知道:「哪裏for」構建被稱爲高級特質界(Magic-of-Rank Trait Bounds,HRTBs),並在此處記錄在https://doc.rust-lang.org/nomicon/hrtb。 HTML – Sebastian