rust方法和关联函数

2023-02-27 17:13:04 浏览数 (1)

Rust方法

在大多数面向对象的语言中都存在方法,方法一般和类关联在一起。在Rust中也是类似的,方法和对象总是一起出现。Rust的方法和结构体,枚举,特征一起使用。

定义方法

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

代码语言:javascript复制
#![allow(unused)]
fn main() {
// 这个圆的结构体定义可以看做是由x,y来定位圆的圆心,radius是圆的半径。
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    // 这种方法往往用于初始化当前结构体的实例
    fn new(x: f64, y: f64, radius: f64) -> Circle {
        Circle {
            x,
            y,
            radius,
        }
    }

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

因此,在rust中定义方法的格式如下:

代码语言:javascript复制
impl 结构体名|枚举名|特征名{
    函数1
    函数2
    ...
}

另外一点需要注意的是,&self实际上是self:&Self的简写,在一个impl块内,Self指代被实现方法的结构体(枚举或者特征)类型,self指代此类型的实例。因此在上面这个例子中,&Self实际指代Circle这个结构体类型,而self则是该结构体的一个实例化对象。实际上和其它面向对象语言是差不多的。(self和C 的this指针实际上是类似的,只不过this是C 方法的隐含参数,而rust的self和python的self在表现形式上几乎是一模一样的。)方法的第一个参数必须有一个名为 self 的Self 类型的参数,所以 Rust 让你在第一个参数位置上只用 self 这个名字来缩写。

还有一点需要注意,那就是new是Circle的关联函数,因为它的第一个参数不是self,且new并不是关键字;而area是Circle的方法,它的第一个参数是&self。

需要注意的是,self 依然有所有权的概念:

  • self 表示 Circle 的所有权转移到该方法中,这种形式用的较少
  • &self 表示该方法对 Circle 的不可变借用
  • &mut self 表示可变借用

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

在 Rust 中,允许方法名跟结构体的字段名相同,一般来说,方法跟字段同名,往往适用于实现get访问器,例如:

代码语言:javascript复制
pub struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    pub fn new(width: u32, height: u32) -> Self {       // Self指代的类型是Rectangle
        Rectangle { width, height }
    }
    pub fn width(&self) -> u32 {
        return self.width;
    }
}

fn main() {
    let rect1 = Rectangle::new(30, 50);

    println!("{}", rect1.width());
}

用这种方式,我们可以把 Rectangle 的字段设置为私有属性,只需把它的 new 和 width 方法设置为公开可见,那么用户就可以创建一个矩形,同时通过访问器 rect1.width() 方法来获取矩形的宽度,因为 width 字段是私有的,当用户访问 rect1.width 字段时,就会报错。

上面这段代码同时展示了关联函数new在使用的时候是结构体名::函数名,而方法则是obj.method。接着在main函数中加上下面两行代码。

代码语言:javascript复制
let rect2 = &Rectangle::new(3, 3);
println!("{}", rect2.width());

可以看到,对于一个引用,我们依旧是采用点(.)运算符来调用width方法的,没有C/C 中的区分,指针使用->,对象使用点(.)。这背后是Rust拥有自动引用和解引用的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。他是这样工作的:当使用 object.something() 调用方法时,Rust 会自动为 object 添加 &、&mut 或 * 以便使 object 与方法签名匹配。也就是说,这些代码是等价的:

代码语言:javascript复制
#![allow(unused)]
fn main() {
#[derive(Debug,Copy,Clone)]
struct Point {
    x: f64,
    y: f64,
}

impl Point {
   fn distance(&self, other: &Point) -> f64 {
       let x_squared = f64::powi(other.x - self.x, 2);
       let y_squared = f64::powi(other.y - self.y, 2);

       f64::sqrt(x_squared   y_squared)
   }
}
let p1 = Point { x: 0.0, y: 0.0 };
let p2 = Point { x: 5.0, y: 6.5 };

// 下面这两行等价,因为Rust会帮我们做自动引用和解引用。
p1.distance(&p2);
(&p1).distance(&p2);
}

多个impl定义

每个结构体都允许拥有多个 impl 块。例如:

代码语言:javascript复制
struct Rectangle {
    width: u32,
    height: u32,
}

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

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

此处没有什么实际意义,只是为了说明rust允许这样的语法,在泛型和trait的时候,这一点会变得有用很多。

关联函数

定义在 impl 中且没有 self 的函数被称之为关联函数,它没有self,不能用obj.method的形式调用,而是使用::的形式来调用,因此他是函数不是方法,而他又在impl中,与结构体紧密关联,因此称为“关联函数”。关联函数在功能上比C 的静态成员函数要灵活的多。

代码语言:javascript复制
Rust 中有一个约定俗成的规则,使用 new 来作为构造器的名称,出于设计上的考虑,Rust 特地没有用 new 作为关键字

参考资料

  1. Rust语言圣经
  2. Rust程序设计语言

0 人点赞