pull/95/head
sunface 3 years ago
parent 86fbd92a45
commit ae5c686c06

@ -35,7 +35,7 @@
- [解构Option](basic/match-pattern/option.md)
- [模式适用场景](basic/match-pattern/pattern-match.md)
- [全模式列表](basic/match-pattern/all-patterns.md)
- [方法Method(todo)](basic/method.md)
- [方法Method](basic/method.md)
- [泛型(todo)](basic/generitic.md)
- [特征(todo)](basic/trait.md)
- [类型转换 todo](basic/type-converse.md)

@ -106,3 +106,74 @@ fn main() {
```
`plus_or_substract`函数根据传入`x`的大小来决定是做加法还是减法,若`x > 5`则通过`return`提前返回`x - 5`的值,否则返回`x + 5`的值。
#### Rust中的特殊返回类型
##### 无返回值`()`
对于Rust新手来说有些返回类型很难理解而且如果你想通过百度或者谷歌去搜索都不好查询因为这些符号太常见了根本难以精确搜索到。
例如元类型`()`,是一个零长度的元组。它没啥作用,但是可以用来表达一个函数没有返回值:
- 函数没有返回值,那么返回一个`()`
- 通过`;`结尾的表达式返回一个`()`
例如下面的`report`函数会隐式返回一个`()`:
```rust
use std::fmt::Debug;
fn report<T: Debug>(item: T) {
println!("{:?}", item);
}
```
与上面的函数返回值相同,但是下面的函数显式的返回了`()`
```rust
fn clear(text: &mut String) -> () {
*text = String::from("");
}
```
在实际编程中,你会经常在错误提示中看到该`()`的身影出没,假如你的函数需要返回一个`u32`值,但是如果你不幸的以`表达式+;`的方式作为函数的最后一行代码,就会报错:
```rust
fn add(x:u32,y:u32) -> u32 {
x + y;
}
```
错误如下:
```console
error[E0308]: mismatched types // 类型不匹配
--> src/main.rs:6:24
|
6 | fn add(x:u32,y:u32) -> u32 {
| --- ^^^ expected `u32`, found `()` // 期望返回u32,却返回()
| |
| implicitly returns `()` as its body has no tail or `return` expression
7 | x + y;
| - help: consider removing this semicolon
```
还记得我们在[语句与表达式](./statement-expression.md)中讲过的吗?只有表达式能返回值,而`;`结尾的是语句在Rust中一定要严格区分表达式和语句的区别这个在其它语言中往往是被忽视的点。
##### 永不返回的函数`!`
感叹号,当用作函数返回值的时候,表示该函数永不返回,特别的,这种语法往往用做会导致程序崩溃的函数:
```rust
fn dead_end() -> ! {
panic!("你已经到了穷途末路,崩溃吧!");
}
```
下面的函数创建了一个无限循环,该循环永不跳出,因此函数也永不返回:
```rust
fn forever() -> ! {
loop {
//...
};
}
```

@ -163,6 +163,8 @@ struct ChangeColorMessage(i32, i32, i32); // 元组结构体
而且从代码规范角度来看,枚举的实现更简洁,代码内聚性更强,不像结构体的实现,分散在各个地方。
## 同一化类型
最后,再用一个实际项目中的设计考虑,来结束枚举类型的语法学习。
例如我们有一个web服务需要接受用户的长连接假设连接有两种TcpStream和TlsStream但是我们希望对这两个连接的处理流程相同也就是用同一个函数来处理这两个连接代码如下:

@ -1,47 +1,264 @@
# function-method.md
# 方法Method
## 函数返回
从面向对象语言过来的同学对于方法肯定不陌生,`class`里面就充斥着方法的概念在Rust中方法的概念也大差不差往往和对象成对出现:
```rust
object.method()
```
例如读取一个文件写入缓冲区,如果用函数的写法`read(f,buffer)`,用方法的写法`f.read(buffer)`. 不过与其它语言`class`跟方法的联动使用不同Rust的方法往往跟结构体、枚举、特征一起使用特征将在后面几章进行介绍。
## 定义方法
Rust使用`impl`来定义方法,例如以下代码:
```rust
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
// new是Circle的关联函数因为它的第一个参数不是self
// 这种方法往往用于初始化当前结构体的实例
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方法定义与其它语言的方法定义做一下对比
<img alt="" src="/img/method-01.png" class="center"/>
可以看出,其它语言中所有定义都在`class`中但是Rust的对象定义和方法定义是分离的这种数据和使用分离的方式会给予使用者极高的灵活度。
再来看一个例子:
```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`相关联的。
接下里的内容非常重要,请大家仔细看。在 `area` 的签名中,有一个我们之前没有看到过的关键字`&self`,该关键字指代的是`&Rectangle`类型,换句话说,`self`指代的是`Rectangle`结构体,这样的写法会让我们的代码简洁很多,而且非常便于理解: 我们为哪个结构体实现方法,那么`self`就是指代的该结构体自身。
需要注意的是,`self`依然有所有权的概念:
- `self`表示`Rectangle`的所有权转移到该方法中,这种形式用的较少
- `&self`表示该方法对`Rectangle`的不可变借用
- `&mut self`表示可变借用
总之,`self`的使用就跟函数参数一样要严格遵守Rust的所有权规则。
回到上面的例子中,选择 `&self` 的理由跟在函数中使用 `&Rectangle` 是相同的:我们并不想获取所有权,也无需去改变它,只是希望能够读取结构体中的数据。如果想要在方法中去改变当前的结构体,需要将第一个参数改为 `&mut self`。通过仅仅使用 `self` 作为第一个参数来使方法获取实例的所有权是很少见的,这种使用方式往往用于把当前的对象转成另外一个对象时使用,转换完后,就不再关注之前的对象,且可以防止对之前对象的误调用。
简单总结下,使用方法代替函数有以下好处:
- 不用在函数签名中重复书写`self`对应的类型
- 代码的组织性和内聚性更强,对于代码维护和阅读来说,好处巨大
#### 方法名跟结构体字段名相同
在Rust中允许方法名跟结构体的字段名相同
```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.witdh`,则是调用它的字段。
一般来说,方法跟字段同名,往往适用于实现`getter`访问器,例如:
```rust
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());
}
```
SPECIAL RETURN TYPES IN RUST
用这种方式,我们可以把`Rectangle`的字段设置为私有属性,只需把它的`new`和`witdh`方法设置为公开可见,那么用户就可以创建一个矩形,同时通过访问器`rect1.width()`方法来获取矩形的宽度, 因为`width`字段是私有的,当用户访问`rect1.witdh`字段时,就会报错。
If you are new to the language, some return types are difficult to interpret. These are also especially difficult to search for because they are made from symbols rather than words.
> ### `->` 运算符到哪去了?
>
> 在 C/C++ 语言中,有两个不同的运算符来调用方法:`.` 直接在对象上调用方法,而 `->` 在一个对象的指针上调用方法,这时需要先解引用指针。换句话说,如果 `object` 是一个指针,那么 `object->something()`和`(*object).something()`是一样的。
>
> Rust 并没有一个与 `->` 等效的运算符相反Rust 有一个叫 **自动引用和解引用**的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。
>
> 他是这样工作的:当使用 `object.something()` 调用方法时Rust 会自动为 `object` 添加 `&`、`&mut` 或 `*` 以便使 `object` 与方法签名匹配。也就是说,这些代码是等价的:
>
> ```rust
> # #[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 };
> p1.distance(&p2);
> (&p1).distance(&p2);
> ```
>
> 第一行看起来简洁的多。这种自动引用的行为之所以有效,是因为方法有一个明确的接收者———— `self` 的类型。在给出接收者和方法名的前提下Rust 可以明确地计算出方法是仅仅读取(`&self`),做出修改(`&mut self`)或者是获取所有权(`self`。事实上Rust 对方法接收者的隐式借用让所有权在实践中更友好。
Known as the unit type, () formally is a zero-length tuple. It is used to express that a function returns no value. Functions that appear to have no return type return (), and expressions that are terminated with a semicolon (;) return (). For example, the report() function in the following code block returns the unit type implicitly:
## 带有多个参数的方法
方法和函数一样,可以使用多个参数:
```rust
use std::fmt::Debug;
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 report<T: Debug>(item: T) {
println!("{:?}", item);
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));
}
```
And this example returns the unit type explicitly:
## 关联函数
现在大家可以思考一个问题,如果为一个结构体定义一个构造器方法?也就是接受几个参数,然后构造并返回该结构体的实例。其实答案在开头的代码片段中就给出了,很简单,不使用`self`中即可。
这种定义在`impl`中且没有`self`的函数被称之为**关联函数** 因为它没有`self`,不能用`f.read()`的形式使用,因此它是一个函数而不是方法,它又在`impl`中,与结构体紧密关联,因此称为关联函数。
在之前的代码中,我们已经多次使用过关联函数,例如`String::from`,用于创建一个动态字符串。
```rust
fn clear(text: &mut String) -> () {
*text = String::from("");
# #[derive(Debug)]
# struct Rectangle {
# width: u32,
# height: u32,
# }
#
impl Rectangle {
fn new(w: u32, h: u32) -> Rectangle {
Rectangle { width: w, height: h }
}
}
```
The unit type often occurs in error messages. Its common to forget that the last expression of a function shouldnt end with a semicolon.
> Rust中有一个约定俗称的规则使用`new`来作为构造器的名称出于设计上的考虑Rust特地没有用`new`作为关键字
The exclamation symbol, !, is known as the “Never” type. Never indicates that a function never returns, especially when it is guaranteed to crash. For example, take this code:
因为是函数,所以不能用`.`的方式来调用,我们需要用`::`来调用,例如 `let sq = Rectangle::new(3,3);`。这个方法位于结构体的命名空间中:`::` 语法用于关联函数和模块创建的命名空间。
## 多个impl定义
Rust允许我们为一个结构体定义多个`impl`块,目的是提供更多的灵活性和代码组织性,例如当方法多了后,可以把相关的方法组织在同个`impl`块中,那么就可以形成多个`impl`块,各自完成一块儿目标:
```rust
fn dead_end() -> ! {
panic!("you have reached a dead end");
# #[derive(Debug)]
# 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
}
}
```
The following example creates an infinite loop that prevents the function from returning:
当然,就这个例子而言,我们没必要使用两个`impl`块,这里只是为了演示方便。
## 为枚举实现方法
枚举类型之所以强大,不仅仅在于它好用、可以[同一化类型](./compound-type/enum.md#同一化类型),还在于,我们可以像结构体一样,为枚举实现方法:
```rust
fn forever() -> ! {
loop {
//...
};
#![allow(unused)]
fn main() {
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
// 在这里定义方法体
}
}
let m = Message::Write(String::from("hello"));
m.call();
}
```
As with the unit type, Never sometimes occurs within error messages. The Rust compiler complains about mismatched types when you forget to add a break in your loop block if youve indicated that the function returns a non-Never type.
除了结构体和枚举,我们还能为特征(trait)实现方法,将在下下章进行讲解,在此之前,先来看看泛型。

BIN
src/img/.DS_Store vendored

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Loading…
Cancel
Save