跟着谷歌安卓团队学Rust - 面向对象的核心结构体的详细入门你想要的都在这里

629 阅读6分钟

大家好,我是梦兽编程。欢迎回来与梦兽编程一起刷Rust的系列。

这是由 Google 的 Android开发团队的分享Rust课程。本课程涵盖了 Rust 的方方面面,从基本语法到泛型和错误处理等高级主题。

该课程的最新版本可以在 google.github.io/comprehensi…

如果你喜欢看梦兽编程的版本可以订阅跟着谷歌安卓团队学Rust订阅最新内容,梦兽编程也期待大家关注我的个人网站。

加入梦兽编程微信群,公众号回复111即可。

面向对象

当我们谈论面向对象编程时,我们关注的是如何组织代码和数据,以及如何使用这些代码和数据。

面向对象编程将数据和操作数据的代码封装在一起,形成一个称为对象的单元。对象可以具有属性(数据)和方法(操作)。通过创建对象,我们可以模拟现实世界中的事物或概念,并通过对象之间的交互来解决问题。

面向对象编程的核心概念包括封装、继承和多态。

面向对象编程的目标是将复杂的问题分解为简单的对象,并通过对象之间的交互来解决问题。它提供了一种组织和管理代码的有效方式,并且可以提高代码的可读性、可维护性和可重用性。

struct

结构体 (struct),Rust 与 golang 语言类似没有提供class关键词。同样提供了struct,但是在rust中通过结构体 (struct) 和 trait(trait)来实现面向对象的特性。

与c++/c不一样的地方是:

Rust引入了所有权系统和借用检查器,以确保内存安全和避免数据竞争。这导致Rust在编写面向对象代码时需要更多的注意事项,例如处理生命周期和借用规则。

封装

struct Circle {
    radius: f64,
}

impl Circle {
    fn new(radius: f64-> Self {
        Circle { radius }
    }

    fn area(&self-> f64 {
        std::f64::consts::PI * self.radius * self.radius
    }
}

fn main() {
    let circle = Circle::new(3.0);
    println!("Area of the circle: {}", circle.area());
}

与其他语言一样,rust中的“类”(class)也是拥有方法的功能。只不过rust中需要使用impl再定义一次。你可以这么认为struct定义“类”的属性。impl实现它的方法。这样做的好处是可以开发分离。如果你要在其他语言系统中实现以上代码大概如下:

// java 已经忘得差不多了不要见怪
class CircleAbstract {
 float radius = 0;
}

class Circle extends CircleAbstract {
  Circle(float radius) {
  this.radius = radius;
 }
 public area() {
  return PI * this.radius * this.radius;
 }
}

如果你有洁癖需要分开两个文件来写的,在其他语言中估计是需要这样实现。这样对比下来是不是感觉rust会相对简单一点?

继承 & 多态

继承是面向对象的第二特性,它允许一个类(称为子类或派生类)从另一个类(称为父类或基类)继承属性和方法。子类可以从父类继承其所有的非私有成员,包括变量和方法。通过继承,子类可以重用父类的代码,并且可以在其基础上进行扩展或修改。

// 这里使用interface是因为多态
interface Animal {
    void makeSound();
}

class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }
}

class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();

        dog.makeSound();
        cat.makeSound();
    }
}

在java语言中,我们需要使用interface来约束未来class的实现。在rust中只不过把interface改成trait,implements改为for。然后继承的写法上是反过来。

trait Animal {
    fn make_sound(&self);
}

struct Dog;

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

struct Cat;

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

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

    dog.make_sound();
    cat.make_sound();
}

多态的作用/好处

在强制类型语言中,多态显得非常重要。如果你使用js/php。这个再使用的过程中完全可以忽略,因为javascript万物皆可json,php中万物皆可array。但是再强制类型语言中,不同的类型是不能放在一起,为了解决这个问题引入了多态。还是上面的的例子进行改造。

我们先定义两个结构体DogCat。然后分别定义一个trait接口Pet,分别继承Pet接口。Vec这个暂时可以了解,这个可以理解成Java里面的ArrayList那玩意,后面进入高级的时候会详细再介绍。

struct Dog { name: String, age: i8 }
struct Cat { lives: i8 } // No name needed, cats won't respond anyway.

trait Pet {
    fn talk(&self-> String;
}

impl Pet for Dog {
    fn talk(&self-> String { format!("Woof, my name is {}!"self.name) }
}

impl Pet for Cat {
    fn talk(&self-> String { String::from("Miau!") }
}

fn main() {
 // 定义一个可变的数组集合
    let petsVec<Box<dyn Pet>> = vec![
        Box::new(Cat { lives: 9 }),
        Box::new(Dog { name: String::from("Fido"), age: 5 }),
    ];
    for pet in pets {
        println!("Hello, who are you? {}", pet.talk());
    }
}

Deriving

在 Rust 中,"Deriving" 是一种自动为结构体或枚举类型实现特定 trait 的机制。通过使用 "derive" 注解,开发人员可以自动为类型实现一些常见的 trait,而不需要手动编写实现代码。

"Deriving" 机制的目的是减少开发人员的重复劳动,使得实现一些常用 trait 的过程更加简单和高效。例如,通过添加 #[derive(Debug)] 注解,Rust 编译器会自动为结构体或枚举类型实现 Debug trait,使得我们可以通过 println! 宏打印出这些类型的调试信息。

除了 Debug trait,Rust 还提供了其他一些可以使用 "derive" 注解自动实现的 trait,如 Clone、Copy、PartialEq、Eq、PartialOrd、Ord 等。这些 trait 在实现某些功能时非常常见,因此通过 "derive" 机制可以大大简化代码编写过程。

需要注意的是,并非所有的 trait 都可以通过 "derive" 机制来自动实现,只有 Rust 标准库中预定义的一些 trait 才支持这种自动实现机制。如果需要实现的 trait 不在预定义的列表中,开发人员仍然需要手动编写实现代码。

#[derive(Debug, Clone, Default)]
struct Player {
    name: String,
    strength: u8,
    hit_points: u8,
}

fn main() {
    let p1 = Player::default(); // Default trait adds `default` constructor.
    let mut p2 = p1.clone();    // Clone trait adds `clone` method.
    p2.name = String::from("EldurScrollz");
    // Debug trait adds support for printing with `{:?}`.
    println!("{:?} vs. {:?}", p1, p2);
}

比如我们在这个结构体中添加了Clone,Default的能力。我们的p1对象就拥有了default设置默认值的方法,还有clone的方法。

总结

以上就是Rust面向对象的所有内容啦,当然这些这是让你快速入门。如果你想了解关于它是如何实现,内存机制等等比较深入的东西。建议你去看《Rust圣经》。

rexweb.link/category/te…]

  1. 梦兽编程倔强号
  2. 梦兽编程知乎
  3. 梦兽编程bilibili

微信搜索梦兽编程公众号,发送111即可加入交流群与梦兽编程一起交流。

参考资料

[1]

google.github.io/comprehensi…: google.github.io/comprehensi…

[2]

跟着谷歌团队学Rust: rexweb.link/category/te…

[3]

梦兽编程: rexweb.link

本文使用 markdown.com.cn 排版