Rust 方法语法:
文章目录
1.前言
在 Rust 中,方法 (Method)是定义在结构体、枚举 或 trait 上下文中 的特殊函数。与函数不同,方法的第一个参数总是 self ,代表调用该方法的结构体实例。本文将深入讲解 Rust 方法的定义、调用方式以及核心特性。
Rust 核心设计:数据与行为分离
Rust 中,结构体( struct )只定义数据, impl 块专门用来给结构体实现方法。所有方法都必须写在 impl 里,不能写在 struct 里。这是 Rust 的"数据与行为分离"设计哲学。
2.正文
方法 与 函数 类似, 它们使用 fn 关键字和名称声明, 可以有形参和返回值. 方法和函数是不同的. 因为他们在结构体的上下文中被定义 或者是枚举 或 trait 对象的上下文. 并且他们的第一个参数总是 self, 他们代表该方法的结构体实例.
现在来写一个定义于 Rectangle 结构体上的 area 方法.
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()
);
}
为了把方法实现在 Rectange 的上下文中, 可以使用 impl 来实现 area 方法.
这里的方法语法就是在 Rectangle 实例上调用 area 方法. 方法语法获取一个实例并加上一个点号, 后面跟上方法名, 圆括号, 以及任何参数.
在 area 的签名中, &self 代表了 rectangle : &Rectangle, &self 实际上是 self : &Self 的缩写. 在 impl 块中, Self 类型是 impl 开的类型的别名. 方法的第一个参数一定要有一个 Self 类型的 self 参数. Rust 可以让你在使用的时候直接使用 self 缩写来实现. 但是这里仍然需要在 self 前面加上 & 来借用 Self 类型的实例. 在上面的代码中没有获取所有权, 但是这里的 self 可以像其他参数一样来获得所有权.
使用方法代替函数, 除了可以使用方法语法不需要在每个函数签名中重复获取 self 的类型, 主要在于组织性.
运算符到哪去了?(引用 wiki 文档)
在 C/C++ 语言中,有两个不同的运算符来调用方法:
.直接在对象上调用方法,而->在一个对象的指针上调用方法,这时需要先解引用(dereference)指针。换句话说,如果object是一个指针,那么object->something()就像(*object).something()一样。Rust 并没有一个与
->等效的运算符;相反,Rust 有一个叫 自动引用和解引用 (automatic referencing and dereferencing)的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。它是这样工作的:当使用
object.something()调用方法时,Rust 会自动为object添加&、&mut或*以便使object与方法签名匹配。也就是说,这些代码是等价的:
rustp1.distance(&p2); (&p1).distance(&p2);第一行看起来简洁的多。这种自动引用的行为之所以有效,是因为方法有一个明确的接收者------------
self的类型。在给出接收者和方法名的前提下,Rust 可以明确地计算出方法是仅仅读取(&self),做出修改(&mut self)或者是获取所有权(self)。事实上,Rust 对方法接收者的隐式借用让所有权在实践中更友好。
带有更多参数的方法
如果实现一个方法让一个 Rectangle 的实例获取另一个 Rectangle 实例,如果 self 能完全包含第二个长方形则返回 true;否则返回 false.
rust
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));
}
上面的代码中还没有实现方法.
现在将方法命名为 can_hold (这里就和我阅读教学文档时定义一样吧) . 它应该位于 impl Rectangle 块中. 方法名是 can_hold 并且他会获取另一个不可变的引用作为参数.
rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
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));
}
can_hold 最后返回一个 bool 值. 实现检查 self 的宽度和高度是否都大于另一个 Rectangle.
多个参数举例:
rust
impl Rectangle {
// 两个额外参数
fn can_hold_with_margin(&self, other: &Rectangle, margin: u32) -> bool {
self.width > other.width + margin && self.height > other.height + margin
}
// 三个额外参数
fn can_hold_in_position(&self, other: &Rectangle, x: u32, y: u32) -> bool {
self.width >= other.width + x && self.height >= other.height + y
}
}
fn main() {
let rect1 = Rectangle { width: 100, height: 100 };
let rect2 = Rectangle { width: 30, height: 40 };
// 调用:self + 2个参数
println!("{}", rect1.can_hold_with_margin(&rect2, 10)); // true
// 调用:self + 3个参数
println!("{}", rect1.can_hold_in_position(&rect2, 20, 30)); // true
}
关联函数
所有在 impl 块中定义的函数被称为关联函数. 可以定义不以 self 为第一参数的关联函数(所以不是一个方法), 应为他们不作用于一个结构体的实例.
关联函数经常被用作返回一个结构体新实例的构造函数。例如我们可以提供一个关联函数,它接受一个维度参数并且同时作为宽和高,这样可以更轻松的创建一个正方形 Rectangle 而不必指定两次同样的值:
rust
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
使用结构体名和 :: 语法来调用这个关联函数:比如 let sq = Rectangle::square(3);。这个方法位于结构体的命名空间中::: 语法用于关联函数和模块创建的命名空间。
多个 impl 块
每个结构体都允许拥有多个 impl 块,但每个方法有其自己的 impl 块。
rust
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 块中,不过这是有效的语法。
3.小结
如果这篇文章帮到了你,不妨:
👍 点赞 ------ 让更多人看到这篇教程
⭐ 收藏 ------ 下次直接翻出来看
💬 评论 ------ 遇到任何问题,评论区交流,我会尽力解答
🔖 关注 ------ 结构体并不是创建自定义类型的唯一方法:让我们转向 Rust 的枚举功能,为你的工具箱再添一个工具。
咱们下篇见!