2016-06-29 12 views
5

Puoi spiegare perché il codice dell'esempio 1 viene compilato, ma l'esempio 2 fornisce un errore di compilazione? Se la spiegazione è troppo lunga e complessa (come sospetto possa spiegare le regole di coerenza), sarei soddisfatto di una semplice risposta come "Credimi, c'è una buona ragione per cui la lingua non consente il codice dell'esempio 2".Perché le regole di coerenza generano l'errore "il parametro type deve essere utilizzato come parametro type per un tipo locale"?

Esempio 1:

use std::ops::Index; 

struct Bounded { 
    idx: usize 
} 

impl Index<Bounded> for [i32; 4] { 
    type Output = i32; 

    fn index(&self, b: Bounded) -> &i32 { 
     unsafe { self.get_unchecked(b.idx) } 
    } 
} 

Esempio 2:

use std::ops::Index; 

struct Bounded { 
    idx: usize 
} 

impl<T> Index<Bounded> for [T; 4] { 
    type Output = T; 

    fn index(&self, b: Bounded) -> &T { 
     unsafe { self.get_unchecked(b.idx) } 
    } 
} 

// error: type parameter `T` must be used as the type parameter for 
// some local type (e.g. `MyStruct<T>`); only traits defined in the 
// current crate can be implemented for a type parameter [--explain E0210] 
+6

Il problema qui è in realtà la regola orfana, non la regola di coerenza :). Puoi controllare la spiegazione con 'rustc --explain E0210'. – kennytm

+1

@kennytm: Non dovrebbe essere una risposta (con qualche elaborazione)? –

risposta

3

lo fa bollire fino a "c'è una buona ragione", ma la buona ragione non è poi così complicato.

Ecco il problema. Immagina di avere una libreria:

// library.rs 
pub struct Dog; 
pub trait Speak { 
    fn speak(&self); 
} 

E due casse che usano quella libreria.

// bark.rs 
extern crate library; 
impl library::Speak for library::Dog { 
    fn speak(&self) { 
     println!("woof"); 
    } 
} 
// woof.rs 
extern crate library; 
impl library::Speak for library::Dog { 
    fn speak(&self) { 
     println!("bark"); 
    } 
} 

Ora, per qualche motivo, voglio usare entrambe queste librerie:

// main.rs 
extern crate library; 
extern crate woof; 
extern crate bark; 

fn main() { 
    let rex = library::Dog; 
    rex.speak(); 
} 

cosa dovrebbe questa uscita del programma? Esistono due implementazioni ugualmente valide e indistinguibili di library::Speak per library::Dog; non c'è una risposta giusta. Quel che è peggio, se dipendevo originariamente da woof e aggiungevo bark in seguito, il mio codice si sarebbe fermato a compilare, o - peggio - iniziare a fare in modo trasparente la cosa sbagliata. I difetti dei tratti conflittuali sono una cosa cattiva ™.

Diventa peggio quando aggiungi generici. Se si dispone di:

// barkgeneric.rs 
extern crate library; 
impl<T> library::Speak for T { 
    fn speak(&self) { 
     println!("woof"); 
    } 
} 
// woofgeneric.rs 
extern crate library; 
impl<T> library::Speak for T { 
    fn speak(&self) { 
     println!("bark"); 
    } 
} 

È ora avere un infinita numero di impls tratto contrastanti. Ops.

Per evitare questo problema, abbiamo le regole orfane. L'idea delle regole orfane è quella di assicurarsi che qualsiasi impl Trait for Type abbia uno, e solo uno, posto dove può essere messo. In questo modo, non dobbiamo preoccuparci dei conflitti impl; dovrebbero essere dritti, se le regole orfane sono state impostate correttamente.

Le regole si riducono a: quando si usa un tratto per il tipo, impl, il carattere o il tratto deve provenire dalla cassa corrente. Ciò rende tutti i miei esempi in conflitto non funzionanti. woof.rs non può implementare library::speak per library::Dog, perché nessuno dei due proviene dalla sua cassa.

Allo stesso modo, non è possibile impl<T> Index<Bounded> for [T; 4];, perché [T; 4] non viene dalla tua cassa, e rustc ha deciso che Index<Bounded> non conta come proveniente dalla cassa sia.

Tuttavia, lascia passare impl Index<Bounded> for [i32; 4] perché in questo caso Index<Bounded> viene da voi.È possibile che si tratti di un bug, ma è anche possibile che sia solo un comportamento previsto; le regole orfane sono leggermente più complesse di quelle che ho affermato qui e potrebbero interagire in modi strani.

Per ulteriori dettagli, vedere rustc --explain E0117, rustc --explain E0210.