【rust语言】rust多态实现方式

文章目录


前言

学习rust当中遇到了这个问题,记录一下,不对地方望指正

一、多态

多态是面向对象程序设计中的一个重要概念,指同一个行为或操作在不同实例上具有不同的行为或结果。简单来说,多态就是指同一种类型的对象,在不同的上下文中有不同的行为。多态性使得程序可以更加灵活、可扩展和易于维护。在实现多态性时,通常会使用继承、接口、抽象类等技术

二、rust实现多态

trait的静态方式

rust 复制代码
trait Animal {
    fn make_sound(&self);
}
struct Cat {}

impl Animal for Cat {
    fn make_sound(&self) {
        println!("Meow");
    }
}
struct Dog {}

impl Animal for Dog {
    fn make_sound(&self) {
        println!("Woof");
    }
}


fn main() {
    let cat: Cat = Cat {};
    let dog: Dog = Dog {};
    test(cat);
    test(dog)
}
//接受Animal Trait类型的
fn test(animal : impl Animal){
    animal.make_sound()
}

定义了方法传入参数是trait。这一种在实例化的时候是具体的类型,在传参的时候编译器能推断出来具体是cat还是dog,能调用具体方法

还有一种方式可以通过动态分发,还以上面那段代码,比如

rust 复制代码
trait Animal {
    fn make_sound(&self);
}

struct Cat {}

impl Animal for Cat {
    fn make_sound(&self) {
        println!("Meow");
    }
}

struct Dog {}

impl Animal for Dog {
    fn make_sound(&self) {
        println!("Woof");
    }
}

fn main() {
    let cat: Box<dyn Animal> = Box::new(Cat {});
    let dog: Box<dyn Animal> = Box::new(Dog {});

    test(cat);
    test(dog)
}

fn test(animal: Box<dyn Animal>) {
    animal.make_sound()
}

这种方式相对于上面更加灵活,因为实例化参数变量类型是trait类型。现在说说关键点

dyn关键字

dyn关键字是在Rust中用于创建和使用动态分发的trait对象的关键字。trait对象允许我们以统一的方式处理不同类型的对象,并使用相同的方法调用语法。使用动态分发,编译器无需在编译时知道具体的类型,而是在运行时根据对象的实际类型来确定要调用的方法。要创建一个trait对象,需要在trait名称前加上dyn关键字。例如,对于名为TraitName的trait,我们可以使用dyn TraitName来创建一个trait对象。

rust 复制代码
trait TraitName {
    // trait定义
}

fn main() {
    let trait_obj: Box<dyn TraitName> = Box::new(ConcreteType);
    // 在这里使用trait对象
}

在上面的代码中,trait_obj是一个Box指向动态分发的trait对象的指针。它可以存储实现了TraitName trait的任何具体类型的对象。通过dyn关键字,我们可以在运行时根据实际类型来调用trait定义的方法。

这里要注意,trait对象通过指针或引用来操作,因此通常结合使用Box、&或&mut来创建和使用trait对象。为了在运行时确定对象的大小,我们需要将它们放置在一个固定大小的容器中。这就是为什么要使用Box来包装trait对象的原因。Box类型表示一个动态分发的trait对象。它在堆上分配一块内存,该内存用于存储对象的数据,并提供一个指向虚函数表(vtable)的指针,该表用于在运行时查找和调用正确的方法。

这种方式相对于静态方式会更加灵活,但会有运行时性能损失,看情况决定使用哪一种

泛型方式

rust 复制代码
use std::fmt::{Display, Formatter};

struct Cat {}

struct Dog {}

impl Display for Cat{
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f,"Cat")
    }
}

impl Display for Dog{
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f,"Dog")
    }
}

fn make_sound<T: Display>(animal: T) {
    println!("{}", animal);
}

fn main() {
    make_sound(Cat{});
    make_sound(Dog{});
}

通过在函数签名中使用泛型类型参数,函数可以接受不同类型的参数,并在编译时生成对应的具体化代码。这种方式不依赖于trait,而是基于类型推断和编译时的静态分发

枚举方式

还有一种方式是使用枚举方式,例如

rust 复制代码
enum Shape {
    Circle(f64),
    Square(f64),
    Rectangle(f64, f64),
}

impl Shape {
    fn area(&self) -> f64 {
        match *self {
            Shape::Circle(radius) => std::f64::consts::PI * radius * radius,
            Shape::Square(side_length) => side_length * side_length,
            Shape::Rectangle(length, width) => length * width,
        }
    }
}

fn main() {
    let circle = Shape::Circle(5.0);
    let square = Shape::Square(4.0);
    let rectangle = Shape::Rectangle(3.0, 6.0);

    test(circle);
    test(square);
    test(rectangle);
}

fn test(shape: Shape) {
    println!("shape area: {}", shape.area());
}

枚举在实现多态性方面有一些优点和缺点。以下是其中的一些:

优点:

简洁性:枚举提供了一种紧凑的方式来定义和组织具有不同变体的数据类型。它能够在一个地方集中描述和管理多种可能的状态或情况。

静态类型检查:由于枚举的变体是预先定义的,编译器可以在编译时验证变体的正确性。这可以帮助捕捉到潜在的错误,并提供类型安全性。

模式匹配:枚举与模式匹配相结合,可以使代码更具表达力和可读性。模式匹配可以根据具体的变体类型执行相应的逻辑,同时处理所有可能的情况,避免遗漏。

缺点:

限制的扩展性:当需要添加新的变体时,枚举需要进行修改。这可能涉及到修改已有的代码,以适应新的变体。这对于外部库或包的枚举类型来说尤其困难,因为无法直接修改其定义。

冗余的结构:枚举的每个变体都可以存储不同的数据结构,这可能会导致某些变体拥有与其他变体不相关的冗余数据。这可能会浪费内存空间,尤其是当只使用其中的一部分变体时。

灵活性的限制:枚举要求提前定义所有可能的变体。如果需要在运行时动态添加新的变体,或者处理不确定的类型集合,那么枚举可能不适合。

总结

以上就是今天要说的内容,不对的地方望指正

相关推荐
喵叔哟11 分钟前
重构代码中引入外部方法和引入本地扩展的区别
java·开发语言·重构
尘浮生18 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
hopetomorrow31 分钟前
学习路之PHP--使用GROUP BY 发生错误 SELECT list is not in GROUP BY clause .......... 解决
开发语言·学习·php
小牛itbull41 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
请叫我欧皇i1 小时前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
闲暇部落1 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
GIS瞧葩菜1 小时前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript
chnming19871 小时前
STL关联式容器之set
开发语言·c++
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
GIS 数据栈1 小时前
每日一书 《基于ArcGIS的Python编程秘笈》
开发语言·python·arcgis