2016-12-05 116 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