2016-12-23 1 views
1

Après avoir lu this answer to "Vector of objects belonging to a trait", il semble que Rust fasse un déballage automatique. Est-ce le cas?Comment déballer des éléments contenus dans des vecteurs polymorphes?

Mon code ne compile pas et je ne comprends pas comment le code de cette réponse pourrait être compilé.

Quelle est la bonne façon de déballer les éléments d'un vecteur polymorphe, l'un contenant des traits encadrés? J'ai lu Rust by Example et the Box documentation et je ne vois aucune méthode qui ressemble à unbox().

Mon code est:

trait HasArea { 
    fn area(&self) -> f64; 
} 

struct Circle { 
    x: f64, 
    y: f64, 
    radius: f64, 
} 

impl HasArea for Circle { 
    fn area(&self) -> f64 { 
     std::f64::consts::PI * (self.radius * self.radius) 
    } 
} 

struct Square { 
    x: f64, 
    y: f64, 
    side: f64, 
} 

impl HasArea for Square { 
    fn area(&self) -> f64 { 
     self.side * self.side 
    } 
} 

fn print_area<T: HasArea>(shape: T) { 
    println!("This shape has an area of {}", shape.area()); 
} 

fn main() { 
    let c = Circle { 
     x: 0.0f64, 
     y: 0.0f64, 
     radius: 1.0f64, 
    }; 

    let s = Square { 
     x: 0.0f64, 
     y: 0.0f64, 
     side: 1.0f64, 
    }; 

    print_area(c); 
    print_area(s); 

    let vec: Vec<Box<HasArea>> = Vec::new(); 
    vec.push(Box::new(c)); 
    vec.push(Box::new(s)); 

    for x in vec { 
     print_area(x) 
    } 
} 

Mon erreur est:

Compiling rustgraph v0.1.0 (file:///home/chris/lunch/rustgraph) 
error[E0277]: the trait bound `Box<HasArea>: HasArea` is not satisfied 
    --> src/main.rs:54:9 
    | 
54 |   print_area(x) 
    |   ^^^^^^^^^^ the trait `HasArea` is not implemented for `Box<HasArea>` 
    | 
    = note: required by `print_area` 

Répondre

6

Vous pouvez déréférencer comme print_area(*x), mais il ne fonctionnera pas pour d'autres raisons: le Sized lié à l'argument print_area . Votre fonction doit connaître la taille de ses arguments.

Vous avez d'autres problèmes dans votre code: vous essayez de pousser dans un vecteur immuable et vous essayez de mettre en boîte des valeurs déplacées. Ils ont été déplacés après l'avoir utilisé dans print_area(). Je pense qu'il serait plus facile de faire print_area une méthode qui prend une référence immuable. Cela fonctionnera comme prévu.

trait HasArea { 
    fn area(&self) -> f64; 
    fn print_area(&self) { 
     println!("This shape has area of {}", self.area()); 
    } 
} 

struct Circle { 
    x: f64, 
    y: f64, 
    radius: f64, 
} 

impl HasArea for Circle { 
    fn area(&self) -> f64 { 
     std::f64::consts::PI * (self.radius * self.radius) 
    } 
} 

struct Square { 
    x: f64, 
    y: f64, 
    side: f64, 
} 

impl HasArea for Square { 
    fn area(&self) -> f64 { 
     self.side * self.side 
    } 
} 

fn print_area<T: HasArea>(shape: &T) { 
    println!("This shape has an area of {}", shape.area()); 
} 

fn main() { 
    let c = Circle { 
     x: 0.0f64, 
     y: 0.0f64, 
     radius: 1.0f64, 
    }; 

    let s = Square { 
     x: 0.0f64, 
     y: 0.0f64, 
     side: 1.0f64, 
    }; 

    c.print_area(); 
    s.print_area(); 

    let mut vec: Vec<Box<HasArea>> = Vec::new(); 
    vec.push(Box::new(c)); 
    vec.push(Box::new(s)); 

    for x in vec { 
     x.print_area(); 
    } 
} 
+0

Merci, ça marche, mais comment le 'x: Box ' dans 'x.print_area();' est-il déballé? Est-ce que le déballage est automatique et où puis-je lire les règles? – fadedbee

+2

C'est juste un déréférencement automatique pour l'appel de méthode, c'est pourquoi rouille n'a pas à la fois les syntaxes 'obj.method()' et 'pointer-> method()', vous pouvez lire ici: https: //doc.rust-lang. org/book/deref-coercions.html – coredump

4

Après avoir lu https://stackoverflow.com/a/25819164/129805 il ressemble à Rust ne unboxing automatique. Est-ce le cas?

Pas aussi automatique que vous pourriez le penser. En fait, vous cherchiez une méthode unbox tandis que Box<T> implémente Deref pour la cible T. Cela signifie que vous devez soit appeler as_ref() ou compter sur la coercition Deref. Notez qu'un T n'est pas possible pour les types non-dimensionnés, et puisque vous comptez sur les types polymorphes, la fonction consommateur devra accepter une référence. J'ai pris la liberté de fixer main et print_area pour le faire fonctionner. Le vecteur a également été incorrectement déclaré comme immuable.

fn print_area<T: HasArea + ?Sized>(shape: &T) { 
    println!("This shape has an area of {}", shape.area()); 
} 

fn main() { 
    let c = Circle { 
     x: 0.0f64, 
     y: 0.0f64, 
     radius: 1.0f64, 
    }; 

    let s = Square { 
     x: 0.0f64, 
     y: 0.0f64, 
     side: 1.0f64, 
    }; 

    print_area(&c); 
    print_area(&s); 

    let mut vec: Vec<Box<HasArea>> = Vec::new(); 
    vec.push(Box::new(c)); 
    vec.push(Box::new(s)); 

    for x in vec { 
     print_area(&*x) 
    } 
} 
2

Comme alternative à ce E_net4 suggéré, au lieu de la boxe vos traits, vous pouvez utiliser un Vec avec des références pour le faire fonctionner:

fn print_area<T: HasArea+?Sized>(shape: &T) { 
    println!("This shape has an area of {}", shape.area()); 
} 

let mut vec: Vec<&HasArea> = Vec::new(); 
vec.push(&c); 
vec.push(&s); 

for x in vec { 
    print_area(x) 
} 
+1

Ma compréhension de la question est que OP voulait vraiment avoir un vecteur propriétaire. –

+0

Oui, je ne fais qu'inclure une manière alternative de gérer un 'Vec 'de traits car il y a d'autres problèmes avec le code OP et ils pourraient vouloir faire des recherches supplémentaires. – ljedrz

+0

Merci pour votre réponse, c'est aussi utile, mais ce que je n'ai vraiment pas compris, c'est que le Rust * faisait un déballage automatique dans la question référencée. Comme vous pouvez le deviner, j'apprends juste Rust. – fadedbee

1

Pour répondre à votre directe question:

Comment déballer des éléments contenus dans des vecteurs polymorphes?

Vous ne pouvez pas. Une fois que quelque chose a été emballé et que le type de béton a été effacé, c'est tout.Un Box<SomeTrait> ne peut pas être retourné dans un SomeConcreteType, parce que rien ne sait ce qu'est ce type concret.


Pour résoudre le problème dans le code ... vérifier le message d'erreur à nouveau:

le trait lié Box<HasArea>: HasArea n'est pas satisfait

En effet, une référence à un trait (ou une boîte d'un trait) does not implement that trait!

Pour que votre programme pour compiler et exécuter que vous avez initialement écrit, il vous suffit de mettre en œuvre le trait pour les boîtes, et nous pourrions aussi bien faire des références aussi:

impl<T: ?Sized> HasArea for Box<T> 
    where T: HasArea 
{ 
    fn area(&self) -> f64 { (**self).area() }  
} 

impl<'a, T: ?Sized> HasArea for &'a T 
    where T: HasArea 
{ 
    fn area(&self) -> f64 { (**self).area() }  
} 

Cela permet à votre plan fixe principale à exécuter:

fn main() { 
    let c = Circle { 
     x: 0.0f64, 
     y: 0.0f64, 
     radius: 1.0f64, 
    }; 

    let s = Square { 
     x: 0.0f64, 
     y: 0.0f64, 
     side: 1.0f64, 
    }; 

    print_area(&c); 
    print_area(&s); 

    let vec: Vec<Box<HasArea>> = vec![Box::new(c), Box::new(s)]; 

    for x in vec { 
     print_area(x) 
    } 
} 

ici, nous passons une référence de c et s-print_area, afin d'éviter le transfert de propriété. Nous utilisons également la macro vec! pour construire le vecteur avec beaucoup moins de cérémonie.