2015-03-21 8 views
14

Sto cercando di implementare una funzione generica in Rust dove l'unico requisito per l'argomento è che l'operazione di moltiplicazione debba essere definita. Sto cercando di implementare un generico "potere", ma andrò con una funzione più semplice cube per illustrare il problema:Richiesta di implementazione di Mul in funzione generica

use std::ops::Mul; 

fn cube<T: Mul>(x: T) -> T { 
    x * x * x 
} 

fn main() { 
    println!("5^3 = {}", cube(5)); 
} 

Quando si compila ottengo questo errore:

error[E0369]: binary operation `*` cannot be applied to type `<T as std::ops::Mul>::Output` 
--> src/main.rs:4:5 
    | 
4 |  x * x * x 
    |  ^^^^^^^^^ 
    | 
    = note: an implementation of `std::ops::Mul` might be missing for `<T as std::ops::Mul>::Output` 

Cosa significa ? Ho scelto il tratto sbagliato? Come posso risolvere questo?

risposta

21

Analizziamo il vostro esempio un po ':

fn cube<T: Mul>(x: T) -> T { 
    let a = x * x; 
    let b = a * x; 
    b 
} 

Quali sono i tipi di a e b? In questo caso, il tipo di a è <T as std::ops::Mul>::Output - suona familiare dal messaggio di errore? Quindi, stiamo tentando di moltiplicare questo tipo di x di nuovo, ma non è possibile garantire che Output possa essere moltiplicato per qualsiasi cosa!

Facciamo la cosa più semplice e dire che T * T necessità di provocare un T:

fn cube<T: Mul<Output = T>>(x: T) -> T { 
    x * x * x 
} 

Purtroppo, questo dà due errori simili:

error[E0382]: use of moved value: `x` 
--> src/lib.rs:6:9 
    | 
6 |  x * x * x 
    |  - ^value used here after move 
    |  | 
    |  value moved here 
    | 
    = note: move occurs because `x` has type `T`, which does not implement the `Copy` trait 

che è a causa della Mul trait takes arguments by value, così abbiamo aggiungere Copy in modo che possiamo duplicare i valori.

Ho anche passato alla clausola where come mi piace di più ed è poco maneggevole da avere più di tanto in linea:

fn cube<T>(x: T) -> T 
where 
    T: Mul<Output = T> + Copy 
{ 
    x * x * x 
} 
11

il limite T: Mul non implica che il risultato del operatore binario è anche di tipo T. Il tipo di risultato è un tipo associato di questo tratto: Output.

L'altro problema è che ad un certo punto nel tempo i tratti dell'operatore sono passati da pass-per-referece a pass-per-valore. Nel codice generico questo può essere un po 'un fastidio nel sedere (almeno per ora) perché questi operatori consumano i propri operandi a meno che non si richieda che i tipi siano Copy.

Solo per completezza (nel caso non si desideri richiedere Copy), aggiungere alcune informazioni su una possibile direzione alternativa.

Per motivi di codice generico, autori di "tipi numerici" sono invitati a fornire ulteriori non consumano implementazioni di questi tratti operatore in modo che non hai bisogno di Copy o Clone. Ad esempio, la libreria standard fornisce già le seguenti implementazioni:

f64 implements Mul< f64> 
f64 implements Mul<&f64> 
&f64 implements Mul< f64> 
&f64 implements Mul<&f64> 

con ogni implementazione avente f64 come Output tipo.Ma facendo uso di questi tratti direttamente non è abbastanza:

fn cube<T>(x: &T) -> T 
    where for<'a> T: Mul<&'a T, Output = T>, 
      for<'a,'b> &'a T: Mul<&'b T, Output = T> 
{ 
    x * x * x 
} 

Alla fine, potremmo ottenere alcuni tratti (leggermente) più alto livello, che ridurrebbe il rumore. Ad esempio: T: Mul2 potrebbe implicare T: Mul<T> + Mul<&T> e &T: Mul<T> + Mul<&T>. Ma al momento di scrivere questo, il compilatore di Rust non sembra in grado di gestirlo. Almeno io non sono riuscito a compilare correttamente il seguente codice:

use std::ops::Mul; 

pub trait Mul2 where 
    Self: Mul<Self, Output=Self>, 
    Self: for<'a> Mul<&'a Self, Output=Self>, 
    for<'a> &'a Self: Mul<Self, Output=Self>, 
    for<'a,'b> &'a Self: Mul<&'b Self, Output=Self> {} 

impl<T> Mul2 for T where 
    T: Mul<T, Output=T>, 
    T: for<'a> Mul<&'a T, Output=T>, 
    for<'a> &'a T: Mul<T, Output=T>, 
    for<'a,'b> &'a T: Mul<&'b T, Output=T> {} 

fn cube<T: Mul2>(x: &T) -> T { 
    x * x * x 
} 

fn main() { 
    let c = cube(&2.3); 
    println!("Hello, world! {}", c) 
} 

Penso che sia giusto dire che le cose miglioreranno in questo settore. Per ora, la capacità di implementare genericamente algoritmi numerici in Rust non è buona come vorrei.

+0

Puoi spiegare anche (o semplicemente dare un link) la notazione "where for", non l'ho mai vista prima. –

+0

dove clausole sono un modo più generale di vincolare i parametri generici. Sono stati introdotti tramite https://github.com/rust-lang/rfcs/blob/master/text/0135-where.md – sellibitze

+0

La notazione 'for <'a>' è stata introdotta in RFC 387: https://github.com /rust-lang/rfcs/blob/master/text/0387-higher-ranked-trait-bounds.md. –

Problemi correlati