【Fitz】Rust 方法 Method

说明:本文主要是Rust语言圣经相关章节的学习笔记,大部分与其内容相同,欢迎阅读原文。

面向对象中的 class 里充斥着方法的概念,而 Rust 的方法往往跟结构体、枚举、Trait 一起使用。

定义方法

Rust 使用 impl 来定义方法,例如:

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    // new是Circle的关联函数,因为它的第一个参数不是self
    // 这种方法往往用于初始化当前结构体的实例
    fn new(x: f64, y: f64, radius: f64) -> Circle {
        Circle {
            x: x,
            y: y,
            radius: radius,
        }
    }

    // Circle的方法,&self表示借用当前的Circle结构体
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

其他语言中类的定义都在类中,而 Rust 的对象定义和方法定义是分离的(即对象定义是 struct A { ... } 而方法定义是 impl A { ... },这种数据和使用分离的方式,会给予使用者极高的灵活度。impl A { ... } 表示为 A 实现方法,这样的写法表明 impl 语句块中的一切都是跟 A 相关联的。

self、&self he &mut self

在上面代码示例 area 的签名中,使用了 &self 作为函数参数,这里 &self 其实是 self: &Self 的简写。在一个 impl 块内, Self 指代被实现方法的结构体类型,self 指代此类型的实例。这样写会让代码简洁很多,而且非常便于理解:我们为哪个结构体实现方法,那么 self 就是指代哪个结构体的实例。

这里的 self 依然有所有权的概念:

  • self 表示结构体的所有权转移到该方法中,这种形式用的较少
  • &self 表示该方法对结构体的不可变引用,可用于读取结构体中的数据
  • &mut self 表示可变引用,可用于改变结构体中的值

self 是使用就跟函数参数一样,要严格遵守 Rust 的所有权规则。仅仅通过使用 self 作为第一个参数来使方法获取实例的所有权是很少见的,这种使用方式往往用于把当前的对象转成另外一个对象时使用,转换完后,就不再关注之前的对象,且可以防止对之前对象的误调用。

使用方法代替函数有以下好处:

  • 不用在函数签名中重复书写 self 对应的类型
  • 代码的组织性和内聚性更强,对于代码维护和阅读来说,好处巨大

方法名跟结构体字段名相同

Rust 允许方法名和结构体字段名相同,如 circle.radius 访问结构体的字段,而 circle.radius() 则是调用其方法。一般来说,方法跟字段同名,往往适用于实现 getter 访问器,来获取结构体中的值。使用这种方式,就可以把结构体的字段设置为私有属性,然后通过访问器来获取内部数据值。

C/C++ 中有两个运算符来调用方法:. 直接在对象上调用方法,而 -> 在对象的指针上调用方法,这时需要先解引用指针。Rust 中并没有一个与 -> 等效的运算符,而是有一个 自动引用和解引用 的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。当使用 object.something() 调用方法时,Rust 会自动为 object 添加 &&mut* 来使 object 与方法签名匹配,Rust 对方法接收者的隐式借用让所有权在实践中更友好。

带有多个参数的方法

方法和函数一样,可以使用多个参数,如:

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

    fn can_hold(&self, other: &Rectangle) -> bool {  //can_hold函数有两个参数
        self.width > other.width && self.height > other.height
    }
}

关联函数

如何为一个结构体定义一个构造器方法?其实前面已经给出,就是不使用 self 参数,接受几个参数,然后构造并返回结构体的实例。

这种定义在 impl 中且没有 self 的函数被称为关联函数:没有 self 不能使用 s.func() 类似的形式调用,因此是一个函数而不是方法,又由于在 impl 中,与结构体紧密关联,因此称为关联函数。因为是函数,所以不能使用 . 的方式来调用,而需要用 :: 来调用,例如 let str = String::from("Hello");:: 语法用于关联函数和模块创建的命名空间。

Rust 中有一个约定俗成的规则,即使用 new 来作为构造器的名称,Rust 也特地没有用 new 作为关键字。

多个 impl 定义

Rust 允许我们为一个结构体定义多个 impl 块,目的是提供更多的灵活性和代码组织性。当方法多了后,可以把相关的方法组织在同一个 impl 块中,就可以形成多个 impl 块,各自完成一块儿目标。

为枚举实现方法

枚举类型之所以强大,不仅仅在于它好用、可以同一化类型,还在于,我们可以像结构体一样,为枚举实现方法:

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

impl Message {
    fn call(&self) {
        // 在这里定义方法体
    }
}

let m = Message::Write(String::from("hello"));
m.call();

除了结构体和枚举,还能为特征(trait)实现方法

posted @ 2022-03-18 16:34  AlphaFitz  阅读(141)  评论(0)    收藏  举报