欢迎来到飞鸟慕鱼博客,开始您的技术之旅!
当前位置: 首页知识笔记正文

学习英语的方法有哪些,学习指导策略和方法

终极管理员 知识笔记 74阅读

文章目录 方法 Method定义方法self、&self 和 &mut self方法名跟结构体字段名相同 带有多个参数的方法关联函数多个 impl 定义为枚举实现方法 rust 结构体与枚举的区别回答1回答2

方法 Method

从面向对象语言过来的同学对于方法肯定不陌生class 里面就充斥着方法的概念。在 Rust 中方法的概念也大差不差往往和对象成对出现

object.method()

例如读取一个文件写入缓冲区如果用函数的写法 read(f, buffer)用方法的写法 f.read(buffer)。不过与其它语言 class 跟方法的联动使用不同这里可能要修改下Rust 的方法往往跟结构体、枚举、特征(Trait)一起使用特征将在后面几章进行介绍。

定义方法

Rust 使用 impl 来定义方法例如以下代码

struct Circle {    x: f64,    y: f64,    radius: f64,}impl Circle {    // new是Circle的关联函数因为它的第一个参数不是self且new并不是关键字    // 这种方法往往用于初始化当前结构体的实例    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 方法定义与其它语言的方法定义做了对比

可以看出其它语言中所有定义都在 class 中但是 Rust 的对象定义和方法定义是分离的这种数据和使用分离的方式会给予使用者极高的灵活度。

再来看一个例子

#[derive(Debug)]struct Rectangle {    width: u32,    height: u32,}impl Rectangle {    fn area(&self) -> u32 {        self.width * self.height    }}fn main() {    let rect1  Rectangle { width: 30, height: 50 };    println!(        The area of the rectangle is {} square pixels.,        rect1.area()    );}

该例子定义了一个 Rectangle 结构体并且在其上定义了一个 area 方法用于计算该矩形的面积。

impl Rectangle {} 表示为 Rectangle 实现方法(impl 是实现 implementation 的缩写)这样的写法表明 impl 语句块中的一切都是跟 Rectangle 相关联的。

self、&self 和 &mut self

接下来的内容非常重要请大家仔细看。在 area 的签名中我们使用 &self 替代 rectangle: &Rectangle&self 其实是 self: &Self 的简写注意大小写。在一个 impl 块内Self 指代被实现方法的结构体类型self 指代此类型的实例换句话说self 指代的是 Rectangle 结构体实例这样的写法会让我们的代码简洁很多而且非常便于理解我们为哪个结构体实现方法那么 self 就是指代哪个结构体的实例。

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

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

总之self 的使用就跟函数参数一样要严格遵守 Rust 的所有权规则。

回到上面的例子中选择 &self 的理由跟在函数中使用 &Rectangle 是相同的我们并不想获取所有权也无需去改变它只是希望能够读取结构体中的数据。如果想要在方法中去改变当前的结构体需要将第一个参数改为 &mut self。仅仅通过使用 self 作为第一个参数来使方法获取实例的所有权是很少见的这种使用方式往往用于把当前的对象转成另外一个对象时使用转换完后就不再关注之前的对象且可以防止对之前对象的误调用。

简单总结下使用方法代替函数有以下好处

不用在函数签名中重复书写 self 对应的类型代码的组织性和内聚性更强对于代码维护和阅读来说好处巨大 方法名跟结构体字段名相同

在 Rust 中允许方法名跟结构体的字段名相同

impl Rectangle {    fn width(&self) -> bool {        self.width > 0    }}fn main() {    let rect1  Rectangle {        width: 30,        height: 50,    };    if rect1.width() {        println!(The rectangle has a nonzero width; it is {}, rect1.width);    }}

当我们使用 rect1.width() 时Rust 知道我们调用的是它的方法如果使用 rect1.width则是访问它的字段。

一般来说方法跟字段同名往往适用于实现 getter 访问器例如:

pub struct Rectangle {    width: u32,    height: u32,}impl Rectangle {    pub fn new(width: u32, height: u32) -> Self {        Rectangle { width, height }    }    pub fn width(&self) -> u32 {        return self.width;    }}fn main() {    let rect1  Rectangle::new(30, 50);    println!({}, rect1.width());}

用这种方式我们可以把 Rectangle 的字段设置为私有属性只需把它的 newwidth 方法设置为公开可见那么用户就可以创建一个矩形同时通过访问器 rect1.width() 方法来获取矩形的宽度因为 width 字段是私有的当用户访问 rect1.width 字段时就会报错。注意在此例中Self 指代的就是被实现方法的结构体 Rectangle

-> 运算符到哪去了

在 C/C 语言中有两个不同的运算符来调用方法. 直接在对象上调用方法而 -> 在一个对象的指针上调用方法这时需要先解引用指针。换句话说如果 object 是一个指针那么 object->something()(*object).something() 是一样的。

Rust 并没有一个与 -> 等效的运算符相反Rust 有一个叫 自动引用和解引用的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。

他是这样工作的当使用 object.something() 调用方法时Rust 会自动为 object 添加 &&mut* 以便使 object 与方法签名匹配。也就是说这些代码是等价的

p1.distance(&p2);(&p1).distance(&p2);

第一行看起来简洁的多。这种自动引用的行为之所以有效是因为方法有一个明确的接收者———— self 的类型。在给出接收者和方法名的前提下Rust 可以明确地计算出方法是仅仅读取&self做出修改&mut self或者是获取所有权self。事实上Rust 对方法接收者的隐式借用让所有权在实践中更友好。

带有多个参数的方法

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

impl Rectangle {    fn area(&self) -> u32 {        self.width * self.height    }    fn can_hold(&self, other: &Rectangle) -> bool {        self.width > other.width && self.height > other.height    }}fn main() {    let rect1  Rectangle { width: 30, height: 50 };    let rect2  Rectangle { width: 10, height: 40 };    let rect3  Rectangle { width: 60, height: 45 };    println!(Can rect1 hold rect2? {}, rect1.can_hold(&rect2));    println!(Can rect1 hold rect3? {}, rect1.can_hold(&rect3));}
关联函数

现在大家可以思考一个问题如何为一个结构体定义一个构造器方法也就是接受几个参数然后构造并返回该结构体的实例。其实答案在开头的代码片段中就给出了很简单参数中不包含 self 即可。

这种定义在 impl 中且没有 self 的函数被称之为关联函数 因为它没有 self不能用 f.read() 的形式调用因此它是一个函数而不是方法它又在 impl 中与结构体紧密关联因此称为关联函数。

在之前的代码中我们已经多次使用过关联函数例如 String::from用于创建一个动态字符串。

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

Rust 中有一个约定俗成的规则使用 new 来作为构造器的名称出于设计上的考虑Rust 特地没有用 new 作为关键字。

因为是函数所以不能用 . 的方式来调用我们需要用 :: 来调用例如 let sq Rectangle::new(3, 3);。这个方法位于结构体的命名空间中:: 语法用于关联函数和模块创建的命名空间。

多个 impl 定义

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

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    }}

当然就这个例子而言我们没必要使用两个 impl 块这里只是为了演示方便。

为枚举实现方法

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

#![allow(unused)]enum Message {    Quit,    Move { x: i32, y: i32 },    Write(String),    ChangeColor(i32, i32, i32),}impl Message {    fn call(&self) {        // 在这里定义方法体    }}fn main() {    let m  Message::Write(String::from(hello));    m.call();}

除了结构体和枚举我们还能为特征(trait)实现方法这将在下一章进行讲解在此之前先来看看泛型。

rust 结构体与枚举的区别

枚举都有方法了那么rust 结构体与枚举有什么区别呢

回答1

使用上枚举定义其中一项就可以使用了每一项之间是相对立的。而结构体主要是为了将有联系的数据放在一起便于将数据归类。

枚举是一个萝卜一个坑但结构体是一块地。

回答2

第一枚举是列举所有可能的成员 而结构体是一个多种类型的数据组合

第二实例化结构体必须为每个成员指定一个具体的值而枚举的成员是各种数据类型包括字符串、数值、结构体甚至另一个枚举通过模式匹配具体的指令

当枚举的成员是非结构体类型时自然清晰了

引用知乎网友回答

标签:
声明:无特别说明,转载请标明本文来源!