2016-01-06 3 views
6

Ich habe eine Fibonacci Struktur, die als Iterator für alles, das One, Zero, Add und Clone implementiert, verwendet werden kann. Dies funktioniert hervorragend für alle Integer-Typen.Wie schreibe ich eine Merkmalsbindung zum Hinzufügen von zwei Referenzen eines generischen Typs?

Ich möchte diese Struktur für BigInteger Typen verwenden, die mit einem Vec implementiert werden und sind teuer zu clone() auf rufen. Ich möchte Add auf zwei Referenzen auf T verwenden, die dann eine neue T zurückgibt (kein Klonen dann).

Für das Leben von mir, dass ich nicht kann man machen, die zwar ...

Arbeits kompiliert:

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

Wunsch:

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

Dies gibt den Fehler

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

Sie könnten interessiert sein an [std :: ops :: AddAssign] (https://doc.rust-lang.org/std/ops/trait.AddAssign.html), deren RFC in der letzten Kommentar-Periode ist: it lässt Sie den "+ =" Operator überladen. Dies würde es ermöglichen, zumindest '.clone()' Aufrufe für die Addition zu vermeiden. –

+0

Das wäre ein Klon weniger :) Ich kann aber beide nicht loswerden. 12+ Wochen bis das, obwohl ich denke .. – dten

+0

Zwei weniger Klone tatsächlich: 'self.next = self.next.clone() + self.curr.clone();' würde durch 'self.next + = & self ersetzt werden. curr; '. –

Antwort

5

Sie waren sehr nah:

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

Sie möchten eine Einschränkung platzieren, dass eine Referenz einer beliebigen Lebenszeit ein Merkmal implementiert. Dies wird (HRTB) genannt.

Wie ich es verstehe, auf den impl die 'a Lebensdauer Platzierung bedeutet, dass der Anrufer des Verfahrens zu bestimmen, das bekommt, was sollte die Lebensdauer sein. Da die Referenz in der Methode verwendet wird, kann der Aufrufer niemals sehen, wie hoch die Lebensdauer sein würde.

+0

Wissen Sie, wie Sie den letzten Klon eliminieren können? Intuitiv würde ich denken, dass die tatsächliche Zahl außerhalb des Iterators liegen müsste, aber ich konnte es nicht funktionieren lassen. –

+0

@MatthieuM. Ich sehe nicht, wie es möglich wäre, diesen Klon zu eliminieren, da der Wert 'current' von der Struktur gespeichert wird, aber wir wollen ihn auch vom Iterator zurückgeben. Mein nächster Gedanke wäre, einen Verweis auf "aktuell" zurückzugeben, aber das würde bedeuten, dass ich ein Leben auf "Selbst" setzen müsste, was ein No-Go ist. Können Sie Ihre Idee "außerhalb des Iterators" erweitern? – Shepmaster

+0

Meine Idee war, einen Zustand 'struct' zu erstellen, um den Zustand zu halten und dann einen anderen Iterator' struct', der auf den ersten ('& mut') Bezug nimmt und ihn so mutiert, dass der Iterator Referenzen in den Zustand zurückbringen kann; aber ich konnte meine Anleihen nicht ausrichten. –

Verwandte Themen