Как мне решить эту проблему наследования в ржавчине? - PullRequest
1 голос
/ 28 мая 2020

прежде всего обязательное «Я новичок в Rust»: Я.

Итак, у меня возникла следующая проблема:

У меня есть две (или более) структуры данных, которые все реализуют какое-то общее поведение в дополнение к своему собственному поведению. У меня есть список этих структур (точнее: «супертипа»), мне нужно получить доступ к некоторым из их общего поведения и некоторым из их индивидуального поведения. Мой вопрос: как мне это сделать в Rust.

Чтобы проиллюстрировать свой вопрос, я придумал сравнение кода между Kotlin и Rust. Kotlin работает так, как я хочу, Rust - нет (пока).

В Kotlin код может выглядеть следующим образом (с использованием простой старой абстракции наследования):

interface Animal {
    fun eat()
    fun sleep()
}
class Cat(val name: String) : Animal {
    fun meow()              { println("meow") }

    override fun eat()      { println("cat $name is eating fish(or lasagne)") }
    override fun sleep()    { println("cat $name sleeps inside") }
}
class Lion(val tag_id: Int) : Animal {
    fun roar()              { println("roar") }

    override fun eat()      { println("lion(tag=${tag_id} is eating gazelle") }
    override fun sleep()    { println("lion(tag=${tag_id} sleeps outside") }
}

var animals: MutableList<Animal> = ArrayList()
fun main() {
    animals.add(Cat("Garfield"))
    animals.add(Lion(12))
    //later:
    for (animal in animals) {
        animal.sleep()
        if (animal is Lion)
            animal.roar()
    }
}

В Rust я придумал следующий код (который не позволяет использовать функцию типа instance_of):

trait Animal {
    fn eat(&self);
    fn sleep(&self);
}

struct Cat {
    name: String
}
impl Cat {
    fn meow(&self)      { println!("meow") }
}
impl Animal for Cat {
    fn eat(&self)       { println!("cat {} is eating fish(or lasagne)", self.name) }
    fn sleep(&self)     { println!("cat {} sleeps inside", self.name) }
}

struct Lion {
    tag_id: usize
}
impl Lion {
    fn roar(&self)      { println!("roar") }
}
impl Animal for Lion {
    fn eat(&self)       { println!("lion(tag={}) is eating fish(or lasagne)", self.tag_id) }
    fn sleep(&self)     { println!("lion(tag={}) sleeps inside", self.tag_id) }
}

fn main() {
    let animals:Vec<Box<dyn Animal>> = vec![
                Box::new(Cat {name: "Garfield".to_string()}),
                Box::new(Lion {tag_id: 12})
    ];
    //later:
    for animal in animals {
        animal.sleep()
        //HOW DO I ACCESS THE CONCRETE STRUCT HERE?
    }
}

Playground

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

Ответы [ 3 ]

1 голос
/ 29 мая 2020

Условия проверки типа могут быть реализованы путем предоставления функции as_<type> для каждого подтипа:

trait Animal {
    fn eat(&self);
    fn sleep(&self);
    fn as_roaring(&self)->Option<&dyn Roaring>;
    fn as_meowing(&self)->Option<&dyn Meowing>;
}

Реализация Lion будет выглядеть так:

impl Animal for Lion {
    fn eat(&self)       { println!("lion(tag={}) is eating fish(or lasagne)", self.tag_id) }
    fn sleep(&self)     { println!("lion(tag={}) sleeps inside", self.tag_id) }
    fn as_roaring(&self)->Option<&dyn Roaring> {Some(self)}
    fn as_meowing(&self)->Option<&dyn Meowing> {None}
}

и l oop:

    for animal in animals {
        animal.sleep();
        if let Some(roaring) = animal.as_roaring() {
          roaring.roar();
        }
    }

Детская площадка .

1 голос
/ 29 мая 2020

Можно попробовать что-то вроде этого

use std::any::Any;

trait Animal {
    fn eat(&self);
    fn sleep(&self);
    fn as_any(&self) -> &dyn Any;
}

struct Cat {
    name: String
}
impl Cat {
    fn meow(&self)      { println!("meow") }
}
impl Animal for Cat {
    fn eat(&self)       { println!("cat {} is eating fish(or lasagne)", self.name) }
    fn sleep(&self)     { println!("cat {} sleeps inside", self.name) }
    fn as_any(&self) -> &dyn Any { self }
}

struct Lion {
    tag_id: usize
}
impl Lion {
    fn roar(&self)      { println!("roar") }
}
impl Animal for Lion {
    fn eat(&self)       { println!("lion(tag={}) is eating fish(or lasagne)", self.tag_id) }
    fn sleep(&self)     { println!("lion(tag={}) sleeps inside", self.tag_id) }
    fn as_any(&self) -> &dyn Any { self }
}

fn main() {
    let animals:Vec<Box<dyn Animal>> = vec![
                Box::new(Cat {name: "Garfield".to_string()}),
                Box::new(Lion {tag_id: 12})
    ];
    //later:
    for animal in animals.iter() {
        animal.sleep();
        if let Some(animal) = animal.as_any().downcast_ref::<Lion>() {
            animal.roar();
        }
    }
}

1 голос
/ 28 мая 2020

Попробуйте сделать это с помощью композиции

trait Animal {
    fn voicebox(&self) -> Voicebox;
}
enum Voicebox {
    CatVoicebox, LionVoicebox
}

impl Voicebox {
    fn make_sound(&self) {
        match *self {
            Voicebox::CatVoicebox => println!("meow"),
            Voicebox::LionVoicebox => println!("roar!")
        }
    }
}

impl Animal for Cat {
    fn voicebox(&self) -> Voicebox {
      Voicebox::CatVoicebox
    }
}

impl Animal for Lion {
    fn voicebox(&self) -> Voicebox {
        Voicebox::LionVoicebox
    }
}

fn main() {
    let animals:Vec<Box<dyn Animal>> = vec![Box::new(Cat {name: "Garfield".to_string()}), Box::new(Lion {tag_id: 12})];
    //later:
    for animal in animals {
        animal.sleep();
        match animal.voicebox() {
            vb@Voicebox::LionVoicebox => vb.make_sound(),
            _ => ()
        }
    }
}

Результат:

cat Garfield sleeps inside
lion(tag=12) sleeps inside
roar!

Rust Детская площадка

...