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
.
Il problema qui è in realtà la regola orfana, non la regola di coerenza :). Puoi controllare la spiegazione con 'rustc --explain E0210'. – kennytm
@kennytm: Non dovrebbe essere una risposta (con qualche elaborazione)? –