2016-01-06 9 views
6

Mam strukturę Fibonacci, która może być używana jako iterator dla wszystkiego, co implementuje One, Zero, i Clone. Działa to doskonale dla wszystkich typów liczb całkowitych.Jak napisać cechę związaną z dodaniem dwóch odniesień typu ogólnego?

Chcę użyć tej struktury dla typów BigInteger, które są zaimplementowane z Vec i są drogie w wywołaniu pod numerem clone(). Chciałbym użyć Add na dwóch odniesieniach do T, który następnie zwraca nowy T (bez klonowania).

Dla życia mnie nie mogę zrobić jeden, który kompiluje chociaż ...

robocza:

extern crate num; 

use std::ops::Add; 
use std::mem; 
use num::traits::{One, Zero}; 

pub struct Fibonacci<T> { 
    curr: T, 
    next: T, 
} 

pub fn new<T: One + Zero>() -> Fibonacci<T> { 
    Fibonacci { 
     curr: T::zero(), 
     next: T::one(), 
    } 
} 

impl<'a, T: Clone + Add<T, Output = T>> Iterator for Fibonacci<T> { 
    type Item = T; 

    fn next(&mut self) -> Option<T> { 
     mem::swap(&mut self.next, &mut self.curr); 
     self.next = self.next.clone() + self.curr.clone(); 
     Some(self.curr.clone()) 
    } 
} 

#[test] 
fn test_fibonacci() { 
    let first_12 = new::<i64>().take(12).collect::<Vec<_>>(); 
    assert_eq!(vec![1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144], first_12); 
} 

Pożądany:

extern crate num; 

use std::ops::Add; 
use std::mem; 
use num::traits::{One, Zero}; 

pub struct Fibonacci<T> { 
    curr: T, 
    next: T, 
} 

pub fn new<T: One + Zero>() -> Fibonacci<T> { 
    Fibonacci { 
     curr: T::zero(), 
     next: T::one(), 
    } 
} 

impl<'a, T: Clone + 'a> Iterator for Fibonacci<T> 
where 
    &'a T: Add<&'a T, Output = T>, 
{ 
    type Item = T; 

    fn next(&mut self) -> Option<T> { 
     mem::swap(&mut self.next, &mut self.curr); 
     self.next = &self.next + &self.curr; 
     Some(self.curr.clone()) 
    } 
} 

#[test] 
fn test_fibonacci() { 
    let first_12 = new::<i64>().take(12).collect::<Vec<_>>(); 
    assert_eq!(vec![1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144], first_12); 
} 

To daje błąd

error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements 
    --> src/main.rs:27:21 
    | 
27 |   self.next = &self.next + &self.curr; 
    |      ^^^^^^^^^^ 
    | 
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 25:5... 
    --> src/main.rs:25:5 
    | 
25 |/ fn next(&mut self) -> Option<T> { 
26 | |   mem::swap(&mut self.next, &mut self.curr); 
27 | |   self.next = &self.next + &self.curr; 
28 | |   Some(self.curr.clone()) 
29 | |  } 
    | |_____^ 
note: ...so that reference does not outlive borrowed content 
    --> src/main.rs:27:21 
    | 
27 |   self.next = &self.next + &self.curr; 
    |      ^^^^^^^^^^ 
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 19:1... 
    --> src/main.rs:19:1 
    | 
19 |/impl<'a, T: Clone + 'a> Iterator for Fibonacci<T> 
20 | | where 
21 | |  &'a T: Add<&'a T, Output = T>, 
22 | | { 
... | 
29 | |  } 
30 | | } 
    | |_^ 
note: ...so that types are compatible (expected std::ops::Add, found std::ops::Add<&'a T>) 
    --> src/main.rs:27:32 
    | 
27 |   self.next = &self.next + &self.curr; 
    |        ^

error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements 
    --> src/main.rs:27:34 
    | 
27 |   self.next = &self.next + &self.curr; 
    |         ^^^^^^^^^^ 
    | 
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 25:5... 
    --> src/main.rs:25:5 
    | 
25 |/ fn next(&mut self) -> Option<T> { 
26 | |   mem::swap(&mut self.next, &mut self.curr); 
27 | |   self.next = &self.next + &self.curr; 
28 | |   Some(self.curr.clone()) 
29 | |  } 
    | |_____^ 
note: ...so that reference does not outlive borrowed content 
    --> src/main.rs:27:34 
    | 
27 |   self.next = &self.next + &self.curr; 
    |         ^^^^^^^^^^ 
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 19:1... 
    --> src/main.rs:19:1 
    | 
19 |/impl<'a, T: Clone + 'a> Iterator for Fibonacci<T> 
20 | | where 
21 | |  &'a T: Add<&'a T, Output = T>, 
22 | | { 
... | 
29 | |  } 
30 | | } 
    | |_^ 
note: ...so that reference does not outlive borrowed content 
    --> src/main.rs:27:34 
    | 
27 |   self.next = &self.next + &self.curr; 
    |         ^^^^^^^^^^ 
+0

Możesz być zainteresowany [std :: ops :: AddAssign] (https://doc.rust-lang.org/std/ops/trait.AddAssign.html), którego RFC znajduje się w Ostatecznym Komentarzu: pozwala przeciążać operatora "+ =". Umożliwiłoby to uniknięcie wywołań '.clone()' przynajmniej dla dodania. –

+0

To byłby jeden klon mniej :) Nie mogę się jednak pozbyć obu. 12+ tygodni do tego, jak sądzę ... – dten

+0

Dwie klony właściwie: 'self.next = self.next.clone() + self.curr.clone();' zostanie zastąpione przez 'self.next + = i self. curr; '. –

Odpowiedz

5

Byłeś bardzo blisko:

impl<T> Iterator for Fibonacci<T> 
where 
    T: Clone, 
    for<'a> &'a T: Add<Output = T>, 
{ 
    type Item = T; 

    fn next(&mut self) -> Option<T> { 
     mem::swap(&mut self.next, &mut self.curr); 
     self.next = &self.next + &self.curr; 
     Some(self.curr.clone()) 
    } 
} 

Chcesz umieścić ograniczenie, że odniesienie do dowolnego okresu życia implementuje daną cechę. Nazywa się to Higher Ranked Trait Bound (HRTB).

Jak rozumiem, umieszczając żywotność 'a na impl oznacza, że ​​rozmówca metody dostaje się do określenia tego, co życie powinno być. Ponieważ odwołanie jest brane pod uwagę w metodzie, osoba dzwoniąca nie może nawet zobaczyć, jakie będzie to życie.

+0

Czy wiesz, jak wyeliminować ostatni klon? Intuicyjnie myślę, że rzeczywista liczba musiałaby być * poza * iteratorem, ale nie mógłbym sprawić, żeby to działało. –

+0

@ MatthieuM. Nie widzę sposobu, w jaki można wyeliminować ten klon, ponieważ wartość 'current' jest zapisywana przez strukturę, ale chcemy również zwrócić ją z iteratora. Następną moją myślą byłoby zwrócenie odniesienia do "prądu", ale wymagałoby to poświęcenia całego życia "jaźni", która nie ma sobie równych. Czy możesz rozwinąć swój pomysł "poza iteratorem"? – Shepmaster

+0

Mój pomysł polegał na stworzeniu stanu 'struct', który utrzymywałby stan, a następnie następnej iteracji' struct' odwołującej się do pierwszej ('& mut') i mutującej ją, gdy" posuwa się naprzód ", aby Iterator mógł zwrócić odniesienia do państwa; ale nie udało mi się zrównoważyć moich pożyczek. –

Powiązane problemy