【问题标题】:How do I solve this inheritance problem in rust?如何在 rust 中解决这个继承问题?
【发布时间】:2020-07-30 07:05:18
【问题描述】:

首先是强制性的“我是 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 思维中”,但我在这里陷入了困境,只需要一点帮助。

【问题讨论】:

标签: kotlin inheritance rust


【解决方案1】:

你可以试试这样的

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();
        }
    }
}

【讨论】:

    【解决方案2】:

    尝试用合成来代替

    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 playground

    【讨论】:

    • 我想我明白了你在做什么,我认为它回答了我的问题,但代码没有按原样编译,游乐场链接又是我的了..
    • @JustinBennett 哦,对不起,我以为它会自动保存。我会更新链接
    • @JustinBennett 我现在修复了链接
    • 非常感谢 - 这绝对是我的问题子集的解决方案。如果我的“子类型”的各个任务是分离的,我该怎么办(即 Cat 还具有单独的方法“run”、“backflip”和“jump”,而 Lion 有“swim”、“walk”和“be_king” ')??
    • @Justin 如果它们如此不同,那你为什么要把它们塞进同一个向量中?总是有可能提出任何给定设计不足的场景。作为设计师,您的任务是确定哪些属性与您要真正解决的问题相关,然后选择具有这些属性的设计。
    【解决方案3】:

    可以通过为每个子类型提供as_&lt;type&gt; 函数来实现类型检查条件:

    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}
    }
    

    和循环:

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

    Playground.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-04
      相关资源
      最近更新 更多