【Rust 中级教程】 02 结构体与泛型

239 阅读4分钟

0x00 开篇

上一篇文章简单介绍了泛型,本篇文章将继续介绍泛型的相关概念——泛型结构体。在介绍泛型结构体前,还需要先介绍下如何在结构体中定义方法,以及泛型方法。本篇文章也算作是对结构体章节的一个补充。

0x01 方法与函数

方法表示某个类型实例的行为,方法与函数的定义大同小异。在前面文章中,不知道你有没有发现,函数和方法这两个词都出现过。在结构体中,方法必须定义在 impl 块中。方法要求第一个参数必须是  &self 或者是  &mut self,这也是与函数的唯一区别。在方法中,使用 &self 读取实例中的数据,使用 &mut self 可以读写实例中的数据。

‍0x02 在结构体中定义方法

先上代码,后解释。

struct Person {
    // 名字
    name: String,
    // 年龄
    age: u8,
}

impl Person {
    // 方法
    fn get_age(&self-> u8 {
        return self.age;
    }

    fn set_age(&mut self, age: u8) {
        self.age = age;
    }

    fn get_name(&self-> &str {
        return self.name.as_str();
    }

    fn set_name(&mut self, name: &str) {
        self.name = name.to_string();
    }

    //关联函数
    fn to_string(person: Person) -> String {
        return format!("Person {{ name: {}, age: {} }}", person.get_name(), person.get_age());
    }
}

fn main() {
    let mut person = Person {
        name: String::from("test"),
        age: 8,
    };

    println!("修改前:name: {}, age: {}", person.get_name(), person.get_age());

    person.set_name("张三");
    person.set_age(18);

    println!("修改后:name: {}, age: {}", person.get_name(), person.get_age());

    // 通过关联函数输出
    println!("{}", Person::to_string(person))

    // 输出结果:
    // 修改前:name: test, age: 8
    // 修改后:name: 张三, age: 18
    // Person { name: 张三, age: 18 }
}

首先我们定义一个结构体 Person,我们通过 impl 为其添加方法。其中,所有方法的第一个参数都是 &self 或者是 &mut self。在 impl 块中,我们定义了 get_age,set_age,get_name,set_name 四个方法。再看 main 函数块内,我们可以通过“ 实例名.方法名”的语法来调用方法。调用方法时,不需要传递 self 参数,self 参数将由编译器自动传递。 另外,我们还在结构体内定义了一个关联函数 to_string 来打印内容。我们通过 “结构体名::函数名”的语法来调用结构体内的关联函数。

0x03 泛型结构体

结构体也可以是泛型的,换句话讲,我们可以使用泛型写一个通用的结构体模板。前面介绍的 Vec 向量就是一个泛型结构体类型,这种结构体可以插入任何数据类型,我们下面以自定义一个“队列”来介绍泛型结构体,先上代码。

/// 定义一个队列
struct Queue<T> {
    // 通过向量存储数据
    data: Vec<T>,
    // 队列的长度
    size: u32,
}

///  定义队列的方法
impl<T> Queue<T> {
    /// 创建一个队列
    /// 这是一个关联函数
    pub fn new() -> Queue<T> {
        Queue {
            data: vec![],
            size: 0,
        }
    }

    /// 向队列中插入一个值
    /// 这是一个方法
    pub fn offer(&mut self, value: T) {
        self.data.push(value);
        self.size += 1;
    }

    /// 弹出队列的最顶部的元素
    /// 这是一个方法
    pub fn poll(&mut self) {
        if self.size > 0 {
            self.data.remove(0);
            self.size -= 1;
        }
    }
}

fn main() {
    let mut queue: Queue<i32> = Queue::new();
    queue.offer(4);
    queue.offer(7);
    queue.offer(10);
    println!("队列的长度:{}", queue.size);
    queue.poll();
    queue.poll();
    queue.poll();
    queue.offer(10);
    println!("队列的长度:{}", queue.size);

    // 输出结果:
    // 队列的长度:3
    // 队列的长度:1
}

首先定义一个队列结构体 Queue ,定义结构体时, 需要在结构体名称后面加上泛型的标签。我们借助向量来存储数据,再额外维护一个队列的长度 size 变量,简单的队列就定义完成。接着定义队列的方法,这里与普通的结构体方法略有不同,我们定义泛型结构体时,需要在 impl 后面加上泛型的标签,然后再跟结构体的名称。在impl 块中,我们分别实现了创建一个队列的关联函数( new)和数据入队(offer),数据出队两个方法(poll)。

0x04 泛型方法

定义:带有泛型类型的参数或者是返回值的方法叫做泛型方法。上面代码中的 offer 方法就是一个泛型方法。泛型方法与泛型函数类似,我就不做介绍了。

0x05 小结

通过两篇文章,介绍了泛型的概念与泛型相关的一些数据类型——泛型与向量,泛型函数,泛型枚举,泛型结构体, 泛型方法等。有关泛型的相关知识基本已经写完了,但是结构体还没有。结构体是 Rust 中很重要的数据类型,它也是 Rust 面向对象的特征中最重要的组成部分。

0x06 本节源码

点击下方“阅读原文”免费获取本节课程源码