基础篇-方法

  • 木头
  • 更新于 2023-02-23 11:04
  • 阅读 1548

定义方法, 带有更多参数的方法,关联函数,多个impl块

方法(method)与函数类似:它们使用 fn 关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码。Rust通过impl关键字在structenum或者trait对象上实现方法调用语法(method call syntax),并且它们第一个参数总是 self,它代表调用该方法的结构体实例。

定义方法

先看一个例子,如何用方法实现计算长方形的面积:

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

// 在 Rectangle 结构体上定义 area 方法
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!("长方形的面积为: {} ", rect1.area());
}

为了使函数定义于Rectangle 的上下文中,我们开始了一个 impl(impl 是 implementation 的缩写),这个 impl 块中的所有内容都将与 Rectangle 类型相关联。在 Rectangle实例上调用 area 方法。方法语法获取一个实例并加上一个点号,后跟方法名、圆括号以及任何参数。

area的签名中,使用&self 来替代 rectangle: &Rectangle&self 实际上是self: &Self 的缩写。在一个 impl 块中,Self 类型是 impl 块的类型的别名。方法的第一个参数必须有一个名为 selfSelf 类型的参数,所以 Rust 让你在第一个参数位置上只用 self 这个名字来缩写。

这里选择 &self 的理由跟在函数版本中使用 &Rectangle 是相同的:我们并不想获取所有权,只希望能够读取结构体中的数据,而不是写入。如果想要在方法中改变调用方法的实例,需要将第一个参数改为 &mut self。通过仅仅使用 self 作为第一个参数来使方法获取实例的所有权是很少见的;这种技术通常用在当方法将 self 转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例。

使用方法替代函数,除了可使用方法语法和不需要在每个函数签名中重复 self 的类型之外,其主要好处在于组织性。我们将某个类型实例能做的所有事情都一起放入 impl 块中,而不是让将来的用户在我们的库中到处寻找 Rectangle 的功能。

请注意,我们可以选择将方法的名称与结构中的一个字段相同。例如,我们可以在 Rectangle 上定义一个方法,并命名为 width

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

impl Rectangle {
    fn width(&self) -> bool {
        self.width > 0
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    if rect1.width() {
        println!("width大于0的值为:{}", rect1.width)
    }
}

在这里,我们选择让 width 方法在实例的 width字段的值大于 0时返回 true,等于 0 时则返回 false,我们可以在同名的方法中使用同名的字段。在 main 中,当我们在 rect1.width 后面加上括号时。Rust 知道我们指的是方法 width。当我们不使用圆括号时,Rust 知道我们指的是字段 width

带有更多参数的方法

我们让一个 Rectangle 的实例获取另一个 Rectangle 实例,如果 self (第一个 Rectangle)长方形面积大于二个长方形面积则返回 true;否则返回 false

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

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

    fn compare(&self, other: &Rectangle) -> bool {
        self.area() > other.area()
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 40,
        height: 50,
    };

    let rect2 = Rectangle {
        width: 30,
        height: 50,
    };

    let rect3 = Rectangle {
        width: 50,
        height: 60,
    };

    println!(
        "第一个长方形面积({})>第二个长方形面积({})={}",
        rect1.area(),
        rect2.area(),
        rect1.compare(&rect2)
    );

    println!(
        "第一个长方形面积({})>第三个长方形面积({})={}",
        rect1.area(),
        rect3.area(),
        rect1.compare(&rect3)
    );
}

输出:

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target/debug/variables`
第一个长方形面积(2000)>第二个长方形面积(1500)=true
第一个长方形面积(2000)>第三个长方形面积(3000)=false

可以在 self 后增加多个参数,而且这些参数就像函数中的参数一样使用。

关联函数

所有在 impl 块中定义的函数被称为 关联函数(associated functions),因为它们与 impl 后面命名的类型相关。我们可以定义不以 self 为第一参数的关联函数(因此不是方法),因为它们并不作用于一个结构体的实例。我们已经使用了一个这样的函数:在 String 类型上定义的 String::from 函数。

不是方法的关联函数经常被用作返回一个结构体新实例的构造函数。这些函数的名称通常为 new ,但 new 并不是一个关键字 ,我们使用 new开头第一个例子:

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

impl Rectangle {
    fn new(width: u32, height: u32) -> Self {
        Rectangle { width, height }
    }

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

fn main() {
    // 方法一   使用关联函数再调用方法
    let rect1 = Rectangle::new(30, 50);
    println!("方法一长方形的面积为: {} ", rect1.area());

    // 方法二   使用关联函数和方法链接
    println!("方法二长方形的面积为: {} ", Rectangle::new(30, 50).area());
}

关键字 Self 在函数的返回类型中代指在 impl 关键字后出现的类型,在这里是 Rectangle

多个 impl 块

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

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

impl Rectangle {
    fn compare(&self, other: &Rectangle) -> bool {
        self.area() > other.area()
    }
}
  • 原创
  • 学分: 4
  • 分类: Rust
  • 标签:
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
124 订阅 31 篇文章

0 条评论

请先 登录 后评论
木头
木头
0xC020...10cf
江湖只有他的大名,没有他的介绍。