Как хранить в векторе разные типы, реализующие один и тот же трейт, и вызывать для них общие функции?

Я изучаю Rust, и у меня возникают трудности с реализацией полиморфизма. Я хочу использовать массив для хранения Circle или Test.

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

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

impl Circle {
    fn new (xx: f64, yy: f64, r: f64) -> Circle{
        Circle{ x: xx, y: yy, radius: r }
    }
}

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

struct Test {
    x:      f64,
    y:      f64,
    radius: f64,
    test:   f64,
}

impl Test {
    fn new (xx: f64, yy: f64, r: f64, t: f64) -> Circle{
        Test{ x: xx, y: yy, radius: r, test: t, }
    }
}

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

Я не знаю, как сделать вектор для хранения типов с теми же trait:

let cir  = Circle::new(10f64, 10f64, 10f64);
let test = Test::new(10f64, 10f64, 10f64, 10f64);

//let mut vec: Vec<Poli> = Vec::new();   <---

Я хотел бы повторить вектор и вызвать функции из черты. Есть ли способ сделать это или какая-то альтернатива?

Я прочитал документацию по трейт-объектам, но думаю, что это не то, что мне нужно.


person Angel Angel    schedule 01.04.2016    source источник
comment
Я не думаю, что это действительно требует полного ответа, но: трейт-объекты — это именно то, что вам здесь нужно (при условии, что вам нужен открытый полиморфизм). Если закрыто приемлемо, вам нужен enum. Если какой-то аспект этой главы вам непонятен, или вы можете объяснить, почему вы считаете это нехорошим решением, добавление этого может улучшить вопрос.   -  person DK.    schedule 01.04.2016
comment
@ДК. извините за мой английский, я не сказал никогда -› почему вы думаете, что это не очень хорошее решение ... просто сказал, что читаю эту страницу и не могу найти то, что искал. получается, что если бы это было на странице, но я не мог хорошо понять примеры, чтобы применить то, что я искал. Спасибо за ваше время   -  person Angel Angel    schedule 01.04.2016


Ответы (2)


Как упоминалось на странице, на которую вы ссылались, вам нужно либо сохранить Poli реализует структуры как Vec<&Poli> или Vec<Box<Poli>> в зависимости от того, хотите ли вы владеть значениями или просто хранить ссылку:

// Owned
let circle = Circle::new(10f64, 10f64, 10f64);
let test = Test::new(10f64, 10f64, 10f64, 10f64);

let polis = vec![Box::new(circle) as Box<Poli>, Box::new(test) as Box<Poli>];

for poli in polis {
    println!("{}", poli.area());
}

// Reference
let circle = Circle::new(10f64, 10f64, 10f64);
let test = Test::new(10f64, 10f64, 10f64, 10f64);

let polis = vec![&circle as &Poli, &test as &Poli];

for poli in polis {
    println!("{}", poli.area());
}

Выход

314.1592653589793
314.1592653589793
314.1592653589793
314.1592653589793

Демо

person Dogbert    schedule 01.04.2016

После ответа Догберта я немного поиграл и обнаружил, что этот способ работает для меня. Я новичок в Rust и не знаю, очевидно ли это, но я оставлю это здесь, чтобы помочь другим.

let circle = Circle::new(10f64, 10f64, 10f64);
let test   = Test::new(10f64, 10f64, 10f64, 10f64);

let mut vec: Vec<&Poli> = Vec::<&Poli>::new();
    vec.push(&circle);
    vec.push(&test);

for v in vec {
    println!("new {}", v.area());
}

Демо

person Angel Angel    schedule 01.04.2016
comment
Обратите внимание, что вам не нужно указывать точный тип дважды: вы можете либо удалить : Vec<&Poli>, либо ::<&Poli>. - person Dogbert; 02.04.2016