百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

Rust面向对象编程(中)

liebian365 2025-03-03 19:16 11 浏览 0 评论

Rust 的封装

封装是面向对象编程的核心之一,通过将数据和操作数据的方法组合在一起,并隐藏实现细节,暴露有限的接口供外部访问。Rust 强调安全性和模块化设计,提供了多种机制来实现封装。

定义结构体和枚举

1. 结构体(Structs)

结构体是用来将多个相关数据组合在一起的自定义数据类型,主要有以下三种形式:

  • 普通结构体:具名字段,每个字段都有一个名字。
  • 元组结构体:字段没有名字,类似于元组,但可以有类型区分。
  • 单元结构体:无字段,用作标记类型。
// 定义一个普通结构体
struct User {
    username: String,
    age: u8,
    email: String,
}

// 定义一个元组结构体
struct Point(f64, f64);

// 定义一个单元结构体
struct Marker;

// 示例:使用结构体
fn main() {
    let user = User {
        username: String::from("Alice"),
        age: 25,
        email: String::from("alice@example.com"),
    };
    println!("User: {}, Age: {}, Email: {}", user.username, user.age, user.email);

    let point = Point(3.5, 7.8);
    println!("Point: ({}, {})", point.0, point.1);

    let _marker = Marker; // 用作标记,无需存储数据
}

2. 枚举(Enums)

枚举是一个类型,它允许我们定义一组可能的值,每个值可以携带额外的信息。它通常用来表示一组有限状态。

示例:

enum Role {
    Admin,
    User(String), // 携带额外信息
    Guest,
}

fn main() {
    let role = Role::User(String::from("Bob"));

    match role {
        Role::Admin => println!("Admin access granted."),
        Role::User(name) => println!("User access granted for: {}", name),
        Role::Guest => println!("Guest access granted."),
    }
}

3. 使用pub关键字控制可见性

Rust 的所有项(字段、方法、模块等)默认是私有的,使用pub关键字可以让它们变为公有,允许其他模块访问。

示例:

struct Account {
    pub username: String, // 公有字段
    balance: f64,         // 私有字段
}

impl Account {
    pub fn new(username: &str, balance: f64) -> Self {
        Account {
            username: username.to_string(),
            balance,
        }
    }

    pub fn get_balance(&self) -> f64 {
        self.balance
    }
}

fn main() {
    let account = Account::new("Alice", 1000.0);
    println!("Account: {}, Balance: {}", account.username, account.get_balance());
}

??注意:

  • pub控制项的可见性,但不改变子字段的默认访问权限。例如,Account的username字段可以公开,但balance仍然是私有的。
  • 使用pub(crate)限定访问范围仅限当前 crate。

模块系统的封装机制

Rust 的模块(mod)系统是封装的重要工具,它允许将代码组织到逻辑单元中,方便管理和控制访问权限。

1. 模块的定义与使用

使用mod关键字定义模块,并通过路径访问其中的内容。

mod library {
    pub struct Book {
        pub title: String,
        author: String, // 私有字段
    }

    impl Book {
        pub fn new(title: &str, author: &str) -> Self {
            Book {
                title: title.to_string(),
                author: author.to_string(),
            }
        }

        pub fn get_author(&self) -> &str {
            &self.author
        }
    }
}

fn main() {
    let book = library::Book::new("Rust Programming", "John Doe");
    println!("Title: {}", book.title);
    println!("Author: {}", book.get_author());
}

2.模块的可见性控制

使用pub关键字可以控制模块内容是否可以从外部访问。

示例:

mod school {
    pub mod classroom {
        pub fn teach() {
            println!("Teaching in the classroom.");
        }
    }

    mod staffroom {
        pub fn discuss() {
            println!("Discussing in the staffroom.");
        }
    }
}

fn main() {
    school::classroom::teach(); // 可访问

    // 无法访问,因为 staffroom 未公开
    // school::staffroom::discuss();
}

3.文件系统中的模块分离

  • 当模块内容较多时,可以将模块定义移至单独的文件中,这里不做演示。

Rust 的继承与组合

Rust 是一门以安全性和性能为核心设计的语言,与传统面向对象语言不同,它不支持继承的概念,而是通过 组合特性(trait) 提供类似继承的行为。

什么是继承与组合?

  • 继承(Inheritance)是指一个类从父类继承数据和行为。它是一种“is-a”的关系,例如“猫是动物”。
  • 组合(Composition)是通过将一个或多个组件嵌套到结构体中来实现代码复用和功能扩展。这是一种“has-a”的关系,例如“车有引擎”。

区别与优势

特性

继承

组合

耦合性

高,子类与父类紧密耦合。

低,各组件相互独立。

灵活性

低,不能动态改变继承关系。

高,可以动态组合功能。

代码复用

简单直接。

需要显式定义组合逻辑。

扩展性

容易引发复杂的继承层次。

更加清晰、易于维护。

Rust 倾向于使用组合实现功能,以避免继承带来的复杂性和潜在问题。

Rust 中没有传统的继承,但可以通过组合实现

在 Rust 中,组合通过将其他结构体作为字段嵌套到一个结构体中来实现。这种方式既保留了模块化,又避免了继承层次的复杂性。

示例:使用组合实现车辆功能

struct Engine {
    horsepower: u32,
}

struct Wheels {
    count: u8,
}

struct Car {
    engine: Engine,
    wheels: Wheels,
}

impl Car {
    fn new(horsepower: u32, wheels: u8) -> Self {
        Car {
            engine: Engine { horsepower },
            wheels: Wheels { count: wheels },
        }
    }

    fn describe(&self) {
        println!(
            "This car has an engine with {} horsepower and {} wheels.",
            self.engine.horsepower, self.wheels.count
        );
    }
}

fn main() {
    let car = Car::new(150, 4);
    car.describe();
}

特点:

  • Car 通过组合 Engine 和 Wheels 实现功能,而非继承。
  • 组件可以独立存在和重用,例如 Engine 还可以用于 Truck 或 Motorcycle。

特性(trait)模拟继承

特性(trait)是 Rust 中定义共享行为的核心机制,可以模拟传统继承中的方法继承。

1. 定义特性和实现

trait Animal {
    fn speak(&self);
}

struct Dog;

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

struct Cat;

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

fn main() {
    let dog = Dog;
    let cat = Cat;

    dog.speak();
    cat.speak();
}

2. 模拟继承的扩展行为

特性可以通过定义默认方法为实现者提供基础行为,类似于继承中重用父类方法。

trait Animal {
    fn speak(&self) {
        println!("This animal makes a sound.");
    }
}

struct Cow;

impl Animal for Cow {
    // 可以选择覆盖默认实现
    fn speak(&self) {
        println!("Moo!");
    }
}

fn main() {
    let cow = Cow;
    cow.speak(); // 输出:Moo!
}

优点:

  • 使用 trait 模拟继承时,只定义需要的行为,避免不必要的耦合。
  • 默认方法提供复用,降低实现成本。

使用泛型约束增强复用性

泛型约束(trait bounds)使得我们可以在泛型函数中约束参数的类型,从而实现更高的代码复用性。

1.泛型函数的实现

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

struct Circle {
    radius: f64,
}

struct Rectangle {
    width: f64,
    height: f64,
}

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

impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

// 泛型函数接受实现了 Shape 特性的类型
fn print_area(shape: &T) {
    println!("The area is: {}", shape.area());
}

fn main() {
    let circle = Circle { radius: 5.0 };
    let rectangle = Rectangle {
        width: 4.0,
        height: 6.0,
    };

    print_area(&circle);
    print_area(&rectangle);
}

2.使用特性约束实现多种行为

trait Fly {
    fn fly(&self);
}

trait Swim {
    fn swim(&self);
}

struct Duck;

impl Fly for Duck {
    fn fly(&self) {
        println!("The duck is flying.");
    }
}

impl Swim for Duck {
    fn swim(&self) {
        println!("The duck is swimming.");
    }
}

fn describe(animal: &T) {
    animal.fly();
    animal.swim();
}

fn main() {
    let duck = Duck;
    describe(&duck);
}

优点:

  • 泛型约束支持多种特性组合,极大增强了代码复用性,同时保持灵活性。

Rust 的多态

多态是面向对象编程的重要特性之一,指一个接口可以表示多种具体类型的行为。Rust 通过静态多态和动态多态实现多态性,但与传统 OOP 语言有所不同。

静态多态与动态多态

1. 静态多态

静态多态是通过 泛型(Generics) 实现的,它在编译时决定具体的类型。这种多态方式高效且无运行时开销。

特点:

  • 编译器在编译期间为每个具体类型生成独立的代码。
  • 无需运行时类型检查,因此性能最佳。
  • 灵活性受限,无法在运行时动态改变行为。

示例:静态多态

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

struct Circle {
    radius: f64,
}

struct Rectangle {
    width: f64,
    height: f64,
}

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

impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

// 使用泛型实现静态多态
fn print_area(shape: &T) {
    println!("The area is: {}", shape.area());
}

fn main() {
    let circle = Circle { radius: 5.0 };
    let rectangle = Rectangle {
        width: 4.0,
        height: 6.0,
    };

    print_area(&circle);
    print_area(&rectangle);
}

2.动态多态

动态多态通过 特性对象(trait objects) 实现。特性对象是指向实现了某个特性的实例的指针,类型在运行时确定。

特点:

  • 支持在运行时动态改变类型的行为。
  • 需要通过虚表(vtable)进行间接调用,因此有一定的运行时开销。
  • 更灵活,适用于需要动态行为的场景。

示例:动态多态

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

struct Circle {
    radius: f64,
}

struct Rectangle {
    width: f64,
    height: f64,
}

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

impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

fn print_area(shape: &dyn Shape) {
    println!("The area is: {}", shape.area());
}

fn main() {
    let circle = Circle { radius: 5.0 };
    let rectangle = Rectangle {
        width: 4.0,
        height: 6.0,
    };

    print_area(&circle);
    print_area(&rectangle);
}

特性对象(trait objects)和 dyn 关键字

1.什么是特性对象?

特性对象(trait object)是对实现特性的实例的动态引用,通常以 &dyn Trait 或 Box 的形式出现。

  • &dyn Trait:不可变引用,类似 &T。
  • Box:指向特性对象的堆分配指针,适合需要动态分配和持久化存储的场景。

2.dyn 关键字的作用

  • 在 Rust 中,dyn 表示动态分发,用来显式地标识一个类型为特性对象。

示例:dyn 的使用

trait Speak {
    fn say(&self);
}

struct Dog;
struct Cat;

impl Speak for Dog {
    fn say(&self) {
        println!("Woof!");
    }
}

impl Speak for Cat {
    fn say(&self) {
        println!("Meow!");
    }
}

fn main() {
    let dog: &dyn Speak = &Dog;
    let cat: &dyn Speak = &Cat;

    dog.say();
    cat.say();
}

3. 使用特性对象实现多态行为

通过特性对象,可以实现更灵活的多态行为。以下是一个图形绘制系统的例子,展示如何使用特性对象实现多态。

示例:图形绘制系统

trait Draw {
    fn draw(&self);
}

struct Circle {
    radius: f64,
}

struct Rectangle {
    width: f64,
    height: f64,
}

impl Draw for Circle {
    fn draw(&self) {
        println!("Drawing a circle with radius: {}", self.radius);
    }
}

impl Draw for Rectangle {
    fn draw(&self) {
        println!(
            "Drawing a rectangle with width: {} and height: {}",
            self.width, self.height
        );
    }
}

fn render(shapes: Vec>) {
    for shape in shapes {
        shape.draw();
    }
}

fn main() {
    let circle = Circle { radius: 10.0 };
    let rectangle = Rectangle {
        width: 15.0,
        height: 20.0,
    };

    // 使用 Box 创建特性对象
    let shapes: Vec> = vec![Box::new(circle), Box::new(rectangle)];

    // 渲染图形
    render(shapes);
}

代码解析:

  • Vec>:动态存储不同类型的图形对象。
  • 特性对象允许存储不同类型的对象,只要它们实现了 Draw 特性。
  • 通过虚表实现动态调用,实现灵活的多态行为。

总结

  • 结构体和枚举是封装的基本单元,用于定义复杂的数据类型。
  • pub关键字用于控制项的可见性,实现接口与实现细节的隔离。
  • 模块系统允许将代码组织成逻辑单元,支持跨文件分离,并通过pub灵活控制模块内容的访问权限。
  • Rust 鼓励组合而非继承:通过嵌套实现代码复用和功能扩展,降低耦合,提高灵活性。
  • 特性模拟继承:通过 trait 定义共享行为,支持默认方法,避免了传统继承的复杂层次。
  • 泛型约束增强复用性:结合特性和泛型,可以实现动态组合行为,简化代码逻辑。
  • 静态多态:基于泛型,在编译时决定类型,无运行时开销,适合性能敏感场景。
  • 动态多态:基于特性对象,通过虚表实现运行时动态行为,适合需要动态扩展的场景。
  • 特性对象与 dyn:dyn 明确表示动态分发,特性对象通过引用或指针管理动态类型。
  • 实现多态行为:通过特性对象,可以在运行时存储和操作多种类型的实例,实现灵活的多态系统。

今天的内容可以说有点多了,但是不碍事,非常感谢你能够看完,如果本文对你有所收获,欢迎关注,点赞收藏和转发,我会继续坚持分享相关的内容!!!

相关推荐

“版本末期”了?下周平衡补丁!国服最强5套牌!上分首选

明天,酒馆战棋就将迎来大更新,也聊了很多天战棋相关的内容了,趁此机会,给兄弟们穿插一篇构筑模式的卡组推荐!老规矩,我们先来看10职业胜率。目前10职业胜率排名与一周前基本类似,没有太多的变化。平衡补丁...

VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符"

首先,程序中头文件的选择,要选择头文件,在文件中是没有对M_PI的定义的。选择:项目——>”XXX属性"——>配置属性——>C/C++——>预处理器——>预处理器定义,...

东营交警实名曝光一批酒驾人员名单 88人受处罚

齐鲁网·闪电新闻5月24日讯酒后驾驶是对自己和他人生命安全极不负责的行为,为守护大家的平安出行路,东营交警一直将酒驾作为重点打击对象。5月23日,东营交警公布最新一批饮酒、醉酒名单。对以下驾驶人醉酒...

Qt界面——搭配QCustomPlot(qt platform)

这是我第一个使用QCustomPlot控件的上位机,通过串口精确的5ms发送一次数据,再将读取的数据绘制到图表中。界面方面,尝试卡片式设计,外加QSS简单的配了个色。QCustomPlot官网:Qt...

大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写

老友相聚,仗剑江湖!《大话西游2》2021全民PK季4月激燃打响,各PK玩法鏖战齐开,零门槛参与热情高涨。PK季期间,不仅各种玩法奖励丰厚,参与PK趣闻录活动,投稿自己在PK季遇到的趣事,还有机会带走...

测试谷歌VS Code AI 编程插件 Gemini Code Assist

用ClaudeSonnet3.7的天气测试编码,让谷歌VSCodeAI编程插件GeminiCodeAssist自动编程。生成的文件在浏览器中的效果如下:(附源代码)VSCode...

顾爷想知道第4.5期 国服便利性到底需优化啥?

前段时间DNF国服推出了名为“阿拉德B计划”的系列改版计划,截至目前我们已经看到了两项实装。不过关于便利性上,国服似乎还有很多路要走。自从顾爷回归DNF以来,几乎每天都在跟我抱怨关于DNF里面各种各样...

掌握Visual Studio项目配置【基础篇】

1.前言VisualStudio是Windows上最常用的C++集成开发环境之一,简称VS。VS功能十分强大,对应的,其配置系统较为复杂。不管是对于初学者还是有一定开发经验的开发者来说,捋清楚VS...

还嫌LED驱动设计套路深?那就来看看这篇文章吧

随着LED在各个领域的不同应用需求,LED驱动电路也在不断进步和发展。本文从LED的特性入手,推导出适合LED的电源驱动类型,再进一步介绍各类LED驱动设计。设计必读:LED四个关键特性特性一:非线...

Visual Studio Community 2022(VS2022)安装图文方法

直接上步骤:1,首先可以下载安装一个VisualStudio安装器,叫做VisualStudioinstaller。这个安装文件很小,很快就安装完成了。2,打开VisualStudioins...

Qt添加MSVC构建套件的方法(qt添加c++11)

前言有些时候,在Windows下因为某些需求需要使用MSVC编译器对程序进行编译,假设我们安装Qt的时候又只是安装了MingW构建套件,那么此时我们该如何给现有的Qt添加一个MSVC构建套件呢?本文以...

Qt为什么站稳c++GUI的top1(qt c)

为什么现在QT越来越成为c++界面编程的第一选择,从事QT编程多年,在这之前做C++界面都是基于MFC。当时为什么会从MFC转到QT?主要原因是MFC开发界面想做得好看一些十分困难,引用第三方基于MF...

qt开发IDE应该选择VS还是qt creator

如果一个公司选择了qt来开发自己的产品,在面临IDE的选择时会出现vs或者qtcreator,选择qt的IDE需要结合产品需求、部署平台、项目定位、程序猿本身和公司战略,因为大的软件产品需要明确IDE...

Qt 5.14.2超详细安装教程,不会来打我

Qt简介Qt(官方发音[kju:t],音同cute)是一个跨平台的C++开库,主要用来开发图形用户界面(GraphicalUserInterface,GUI)程序。Qt是纯C++开...

Cygwin配置与使用(四)——VI字体和颜色的配置

简介:VI的操作模式,基本上VI可以分为三种状态,分别是命令模式(commandmode)、插入模式(Insertmode)和底行模式(lastlinemode),各模式的功能区分如下:1)...

取消回复欢迎 发表评论: