2017-08-11 1 views
2

Vor einiger Zeit habe ich einen generischen Iterator für die Fibonacci-Sequenz geschrieben, der sowohl primitive Zahlen als auch benutzerdefinierte Typen akzeptieren könnte (wie bignums). Nachdem es nicht gelungen, eine Version zum Laufen zu bringen für beide primitiven Typen und bignums, ich auf dieser Frage gestolpert:Rust generic AddAssign mit Referenzen

How to write a trait bound for adding two references of a generic type?

die so genannten gebrauchten höherrangigen Trait Bounds das Problem mit diesem besonderen Problem zu lösen .

Jetzt versuche ich jedoch, eine ähnliche Strategie zu verwenden, anstatt die Operatoren zu verwenden. Insbesondere, ich versuche, etwas ähnliches zu dieser Funktion zu erhalten:

use std::ops::{Add, AddAssign}; 

fn add_test<'a, T>(x: &'a T, y: &'a T) -> T 
where 
    for<'b> &'b T: Add<Output = T>, 
{ 
    x + y 
} 

fn add_assign_test<'a, T>(x: &'a mut T, y: &'a T) -> T 
where 
    for<'b> &'b mut T: AddAssign<&'b T>, 
    T: Clone, 
{ 
    x += y; 
    x.clone() 
} 

fn main() { 
    println!("add_test()={}", add_test(&1, &2)); 
    println!("add_assign_test()={}", add_assign_test(&mut 2, &2)); 
} 

add_test() funktioniert wie erwartet, aber ich bin nicht in der Lage add_assign_test() zu erhalten in ähnlicher Art und Weise zu arbeiten. Die Fehler erhalte ich vorschlagen, dass es vielleicht nicht wirklich eine Implementierung für diese Art von Verhalten auf den primitiven Typen existieren:

error[E0277]: the trait bound `for<'b> &'b mut _: std::ops::AddAssign<&'b _>` is not satisfied 
    --> src/main.rs:21:38 
    | 
21 |  println!("add_assign_test()={}", add_assign_test(&mut 2, &2)); 
    |          ^^^^^^^^^^^^^^^ no implementation for `&'b mut _ += &'b _` 
    | 
    = help: the trait `for<'b> std::ops::AddAssign<&'b _>` is not implemented for `&'b mut _` 
    = note: required by `add_assign_test` 

ich ein Makro erstellen können, die Implementierungen für diese Operatoren erzeugt, die Verweise auf die primitive tatsächlich nimmt Arten, aber das scheint ein wenig verschwenderisch. Gibt es eine andere Möglichkeit, denselben Effekt zu erzielen?

Antwort

1

Nur ein kleiner Fehler in Ihrem Code. Schauen wir uns die Eigenschaft aussehen:

pub trait AddAssign<Rhs = Self> { 
    fn add_assign(&mut self, rhs: Rhs); 
} 

Der Empfänger des Verfahrens bereits &mut self und nicht self. Der Grund, dass Sie die zusätzliche Arbeit mit Add zu tun hatten, ist, weil es self als Empfänger akzeptiert. Für AddAssign bedeutet dies: Wenn ein Typ TAddAssign implementiert, können Sie die Methode add_assign() auf einem &mut T aufrufen!

So kann anstelle des Schreibens:

where for <'b> &'b mut T: AddAssign<&'b T>, 

... Sie würde schreiben:

where for <'b> T: AddAssign<&'b T>, 

(Keine andere Linie geändert bisher)


Sie beachten jedoch, dass der Code still won't compile:

0 ist 12315518370584377839521010

Der Grund ist einfach: Es gibt einfach keine Implementierung von AddAssign für primitive Typen, die eine unveränderliche Referenz als rhs (Docs) nimmt. Ich weiß nicht, ob das ein Versehen ist - es könnte sich lohnen, ein Thema im Rust-Repo zu eröffnen.

Um zu überprüfen, ob der obige Code funktioniert, schreibe ich meinen eigenen Typ und implementiert AddAssign entsprechend: Playground.

+0

Hmm, das war ziemlich viel, was ich nicht wollte :( Mit Blick auf den Rost Repo-Karten hören, ich denke, das ist das gleiche Problem wie: https://github.com/rust- lang/rost/issues/32094 right? – Xaldew

+0

@Xaldew Ja, das scheint das Problem zu sein ... Das ist ein bisschen enttäuschend, in der Tat ... –

+0

Blick auf die https://blog.rust-lang.org/2017/11/ 22/Rust-1.22.html ändert sich, es sieht so aus, als ob das jetzt tatsächlich behoben wurde.Nur eine Kleinigkeit, die mir aufgefallen ist, als ich das noch einmal gelesen habe: Warum muss ich die linke Seite dereferenzieren? – Xaldew

0

den Code-Snippet wird korrigiert basierend auf Lukas' Antwort:

use std::ops::{Add, AddAssign}; 

fn add_test<'a, T>(x: &'a T, y: &'a T) -> T 
where 
    for<'b> &'b T: Add<Output = T>, 
{ 
    x + y 
} 

fn add_assign_test<'a, T>(x: &'a mut T, y: &'a T) -> T 
where 
    for<'b> T: AddAssign<&'b T>, 
    T: Clone, 
{ 
    *x += y; 
    x.clone() 
} 

fn main() { 
    println!("add_test()={}", add_test(&1, &2)); 
    println!("add_assign_test()={}", add_assign_test(&mut 2, &2)); 
} 

Es scheint, dass dies ein Problem in Rust selbst ist. Es gibt derzeit einen Tracker for this issue.

Bis dies festgelegt ist, gibt es zwei mögliche Lösungen:

  1. Erstellen genannt Lings für jede Primitive und implementieren OpAssign für diese Typen statt. Dies zwingt Sie jedoch dazu, alle Grundelemente auf Ihren benutzerdefinierten Typ zu übertragen.

  2. Duplizieren Sie den 'generischen' Code mit Spezialisierungen für die Primitiven.