前言#
昨天我们用一个小栗子对struct加深了印象,今天我们继续往下学。
方法语法(Method Syntax)#
这标题怪怪的。。。
方法methods和函数functions的用法一样,都是fn关键字声明。
而他俩的不同点则是methods需要被定义在struct、enum(枚举)、trait object中。
methods的第一个参数一定是self,这个self指向调用这个方法的struct实例上下文,相当于js中的this。
定义一个method#
还记得我们昨天写的栗子吗?我们最后声明了一个Rect的struct,将width和height关联到rect矩形上,然后我们又写了一个rect的方法用来计算面积area。
struct Rect {
width: f64,
height: f64,
}
fn main () {
let rect = Rect {
width: 10.0,
height: 12.0,
};
dbg!(&rect);
}
fn area (rect: &Rect) -> f64 {
rect.width * rect.height
} 但其实矩形的面积以及矩形的面积计算方法都是和矩形有关的,所以把矩形的面积以及计算面积的方法放到struct上。
我们先来看下代码
#[derive(Debug)]
struct Rect {
width: f64,
height: f64,
area: f64,
}
impl Rect {
fn area (&self) -> f64 {
self.width * self.height
}
}
fn main () {
let mut rect = Rect {
width: 10.0,
height: 12.0,
area: 0.0,
};
let res = rect.area();
rect.area = res;
dbg!(&rect);
} 
可以看到,定义method的关键字是impl,不过不是写在method前面的,而是给struct的,毕竟这是一个struct里的method。
同时在调用方法时不用再传入参数width和height了,因为这俩属性是他自己的,可以再self(this)上面直接拿到。
有一点你应该注意到了,那就是&self,而不是self: &Rect,这是一种语法糖写法。对于methods来说定义self类型是没必要的,它本身就被定义在struct中,调用它的自然得是它的instance,类型早就固定下来了。当然,你也可以写上。
area属性我们是在获取area方法返回的值之后再赋的值,这样可以,不过还有另外一种方式,可以将area属性放到area这个method里面去赋值。
#[derive(Debug)]
struct Rect {
width: f64,
height: f64,
area: f64,
}
impl Rect {
fn area (&mut self) {
self.area = self.width * self.height;
}
}
fn main () {
let mut rect = Rect {
width: 10.0,
height: 12.0,
area: 0.0,
};
rect.area();
dbg!(&rect);
}不过就代码可读性来说会差一些,毕竟赋值是在method中。
另外还有个点说下,那就是属性和方法名是可以一样的,比如:
#[derive(Debug)]
struct Rect {
width: f64,
height: f64,
area: f64,
}
impl Rect {
fn width (&self) -> bool {
self.width > 0.0
}
}
fn main () {
let mut rect = Rect {
width: 10.0,
height: 12.0,
area: 0.0,
};
if rect.width() {
dbg!(&rect);
}
}我们声明了一个width的方法,返回是否有width的判断结果。
关于rust中用点表示法调用方法#
在C/C++中,调用一个方法有两种方式:
- 也是点表示法调用方法,但是只能是用在对象本身的调用上;
- 如果是顺着指针调用方法,那么就得用
->来调用,比如:object_pointer -> get_data().类似(*object_pointer).get_data()解引用。
但是在rust中就没这个必要, rust会判断当前情况来自动加上&引用或者解引用(dereference)。 比如:
p1.distance(&p2);
(&p1).distance(&p2);它俩是一样的。
如果还是不明白的话,我们来看下我们的例子

我们并没有用&rect.area(),但实际上并没报错。
关联函数(associated functions)#
struct这玩意儿长得像一个类,那么一个类怎么能少了构造函数呢?rust中这种类似构造函数的东西叫做关联函数,它们同样定义在struct中,但是它们的第一个参数并不是实例上下文self,这也就说明struct的实例并不能用点表示法调用这些方法。
另外它们的调用方法是用::
最常见的就是我们的String::from和String::new了,他们都会返回一个字符串实例。而这些字符串实例并不能再.new这样调用。
我们来看个例子
#[derive(Debug)]
struct Rect {
width: f64,
height: f64,
area: f64,
}
impl Rect {
fn area (&mut self) {
self.area = self.width * self.height;
}
fn width (&self) -> bool {
self.width > 0.0
}
fn create (width: f64, height: f64) -> Rect {
Rect {
width,
height,
area: 0.0
}
}
}
fn main () {
let mut rect = Rect::create(10.0, 12.0);
rect.area();
if rect.width() {
dbg!(&rect);
}
}Rect::create就是一个典型的关联函数,它返回一个Rect类型的实例, 可以看到create方法它返回的是一个实例并且他的第一个参数并不是self。
另外还可以这样写
fn create (width: f64, height: f64) -> Self {
Self {
width,
height,
area: 0.0
}
}
Self`类型等价于当前的`Rect最后说一下impl允许同时存在多个,你可以把一整块impl作用域拆分为好几块,这样可阅读性会更强(前提是代码量巨大)
总结#
今天我们学了如何定义一个method以及定义associated functions构造函数。
如果觉得对你有用的话,麻烦点个赞,谢谢~
参考#
- ^rust-method-syntax https://doc.rust-lang.org/book/ch05-03-method-syntax.html#method-syntax
- ^rust-define-method https://doc.rust-lang.org/book/ch05-03-method-syntax.html#defining-methods
- ^rust-associated-functions https://doc.rust-lang.org/book/ch05-03-method-syntax.html#associated-functions
发布于 2022-12-06 21:38・IP 属地广东
