[易学易懂系列|rustlang语言|零基础|快速入门|(7)|函数Functions与闭包Closure]

释放双眼,带上耳机,听听看~!

[易学易懂系列|rustlang语言|零基础|快速入门|(7)函数Functions与闭包Closure]

有意思的基础知识

函数Functions与闭包Closure


我们今天再来看看函数。

在Rust,函数由关键词:fn来定义。

如果有参数,必须定义参数的数据类型。

一般情况下,函数返回元组(
tuple
)类型,如果要返回特定的类型,一般要用符号:

->
来定义。

请看代码如下:

1.main函数:


1
2
3
4
5
6
1fn main() {  
2   println!("Hello, world!");  
3}  
4​  
5​
6

2.传递参数:


1
2
3
4
1fn print_sum(a: i8, b: i8) {  
2   println!("sum is: {}", a + b);  
3}
4

3.有返回值:


1
2
3
4
5
6
7
8
9
10
11
12
1/ 01. Without the return keyword. Only last expression returns.  
2fn plus_one(a: i32) -> i32 {  
3   a + 1  
4   // There is no ending ; in the above line. It means this is an expression which equals to `return a+1;`  
5}  
6​  
7// 02. With the return keyword.  
8fn plus_two(a: i32) -> i32 {  
9   return a + 2; // Returns a+2. But, this's a bad practice.  
10   // Should use only on conditional returns, except in the last expression  
11}
12

4.函数指针,作为数据类型:


1
2
3
4
5
6
7
8
1// 01. Without type declarations  
2let b = plus_one;  
3let c = b(5); //6  
4​  
5// 02. With type declarations  
6let b: fn(i32) -> i32 = plus_one;  
7let c = b(5); //6
8

闭包:

1.闭包,即匿名函数或lambda函数。

2.参数类型与返回值,是可选的。

闭包,一句话来说,就是特殊的函数。

首先我们来看看正常函数:


1
2
3
4
5
6
7
8
9
1fn main() {  
2 let x = 2;  
3 println!("{}", get_square_value(x));  
4}  
5​  
6fn get_square_value(x: i32) -> i32 {  
7   x * x  
8}
9

然后,我们用闭包来改写:


1
2
3
4
5
6
7
8
9
1fn main() {  
2   let x = 2;  
3   let square = |x: i32| -> i32 { // Input parameters are passed inside | | and expression body is wrapped within { }  
4       x * x  
5   };  
6   println!("{}", square(x));  
7}  
8​
9

进一步简写:


1
2
3
4
5
6
1fn main() {  
2   let x = 2;  
3   let square = |x| x * x; // { } are optional for single-lined closures  
4   println!("{}", square(x));  
5}
6

我们来简单对比一下函数与闭包,请看下面程序 :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1fn main() {  
2   // 函数形式:累加1.  
3   fn function(i: i32) -> i32 {  
4       i + 1  
5   }  
6​  
7   //闭包形式:完整定义  
8   let closure_annotated = |i: i32| -> i32 { i + 1 };  
9   //闭包形式:简化定义,利用rust的类型推导功能自动进行类型推导,这个更常用  
10   let closure_inferred = |i| i + 1;  
11   let i = 1;  
12​  
13   // 调用函数与闭包  
14   println!("function: {}", function(i));  
15   println!("closure_annotated: {}", closure_annotated(i));  
16   println!("closure_inferred: {}", closure_inferred(i));  
17​  
18   //简单的闭包,只返回一个integer值,返回值 类型将如系统自动推导,即系统对1这个数字,自动判断,并推导出它是integer  
19   let one = || 1;  
20   println!("closure returning one: {}", one());  
21}  
22​
23

我们看到,函数定义更复杂。

我们再来看看下面的程序,比对一下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1fn main() {  
2     
3   let x = 4;//定义一个integer变量  
4   // 函数形式:累加.  
5   fn function(i: i32) -> i32 {  
6       i + x//!!!!!这里编译报错!!!,函数不能从运行环境上获取其他变量!!!!  
7   }  
8​  
9   //闭包形式:完整定义  
10   let closure_annotated = |i: i32| -> i32 { i + x };//用闭包可以从环境中获取x变量  
11   //闭包形式:简化定义,这个更常用  
12   let closure_inferred = |i| i + x;  
13   let i = 1;  
14​  
15   // 调用函数与闭包  
16   println!("function: {}", function(i));  
17   println!("closure_annotated: {}", closure_annotated(i));  
18   println!("closure_inferred: {}", closure_inferred(i));  
19​  
20   //简单的闭包,只返回一个integer值,返回值 类型将如系统自动推导,即系统对1这个数字,自动判断,并推导出它是integer  
21   let one = || 1;  
22   println!("closure returning one: {}", one());  
23}  
24​
25

编译器报错!

编译器详细错误信息为:


1
2
3
4
5
6
7
8
1error[E0434]: can't capture dynamic environment in a fn item  
2--> src\main.rs:5:13  
3|  
45 |         i + x  
5|             ^  
6|  
7= help: use the `|| { ... }` closure form instead
8

这里的编译器详细指出:函数不能动态从环境(当前运行环境或上下文)获得x,并提示用闭包。

我们再来看看如下代码:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
1fn main() {  
2   use std::mem;  
3   let color = "green";  
4​  
5   // A closure to print `color` which immediately borrows (`&`) `color` and  
6   // stores the borrow and closure in the `print` variable. It will remain  
7   // borrowed until `print` is used the last time.  
8   //  
9   // `println!` only requires arguments by immutable reference so it doesn't  
10   // impose anything more restrictive.  
11   //定义一个简单的打印闭包,直接共享借用color变量,并把闭包绑定到print变量  
12   let print = || println!("`color`: {}", color);  
13​  
14   // Call the closure using the borrow.  
15   //直接调用这个闭包(借用color)  
16   print();  
17​  
18   // `color` can be borrowed immutably again, because the closure only holds  
19   // an immutable reference to `color`.  
20   //color变量可以再次共享借用_reborrow,因为闭包print只是用了共享借用color  
21   let _reborrow = &color;  
22   print();  
23​  
24   // A move or reborrow is allowed after the final use of `print`  
25   //上面调用 了print()闭包后,这个color可以移动move(即转移其数据所有权)  
26   let _color_moved = color;  
27​  
28   let mut count = 0;  
29   // A closure to increment `count` could take either `&mut count` or `count`  
30   // but `&mut count` is less restrictive so it takes that. Immediately  
31   // borrows `count`.  
32   //  
33   // A `mut` is required on `inc` because a `&mut` is stored inside. Thus,  
34   // calling the closure mutates the closure which requires a `mut`.  
35   //这里用可变借用变量count, 所以闭包也定义为可变的引用  
36   let mut inc = || {  
37       count += 1;  
38       println!("`count`: {}", count);  
39   };  
40​  
41   // Call the closure using a mutable borrow.  
42   //通过可变借用调用闭包  
43   inc();  
44​  
45   // The closure still mutably borrows `count` because it is called later.  
46   // An attempt to reborrow will lead to an error.  
47   // let _reborrow = &count;//这里如果共享借用将报错,因为count已经可变借用给闭包,再借用将通不过编译器  
48   // ^ TODO: try uncommenting this line.  
49   inc();  
50​  
51   // The closure no longer needs to borrow `&mut count`. Therefore, it is  
52   // possible to reborrow without an error  
53   //这个语句下面,没有再调用inc()闭包的代码,即现在闭包没有再可变借用变更count,现在就可以用可变借用来借用count  
54   let _count_reborrowed = &mut count;  
55​  
56   // A non-copy type.  
57   //定义一个引用变更或智能指针变量(非复制类型)  
58   let movable = Box::new(3);  
59​  
60   // `mem::drop` requires `T` so this must take by value. A copy type  
61   // would copy into the closure leaving the original untouched.  
62   // A non-copy must move and so `movable` immediately moves into  
63   // the closure.  
64   //定义一个闭包,把变量movable移动到闭包里,用方法mem::drop直接把变量内存释放  
65   let consume = || {  
66       println!("`movable`: {:?}", movable);  
67       mem::drop(movable);  
68   };  
69​  
70   // `consume` consumes the variable so this can only be called once.  
71   //调用闭包方consume直接把变量释放掉,所以这个闭包只能调用一次  
72   consume();  
73   // consume();//第二次调用会报错!  
74   // ^ TODO: Try uncommenting this lines.  
75}  
76​
77

上面的代码用来以下两个标准库

Box

std::mem::drop

以上代码打印结果为:


1
2
3
4
5
6
1`color`: green  
2`color`: green  
3`count`: 1  
4`count`: 2  
5`movable`:
6

我们再来看看更复杂的例子,把闭包当作一个输入参数:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
1// A function which takes a closure as an argument and calls it.  
2// <F> denotes that F is a "Generic type parameter"  
3//定义一个函数apply,其参数为:F类型的闭包  
4fn apply<F>(f: F)  
5where  
6   // The closure takes no input and returns nothing.  
7   //这里指定F类型的闭包为FnOnce类型,即它只能调用一次  
8   //这个闭包没有参数与没有返回值  
9   F: FnOnce(),  
10{  
11   // ^ TODO: Try changing this to `Fn` or `FnMut`.  
12   //可以尝试改变这个F类型为 Fn(不可变函数)或FnMut(可变函数)  
13   f();  
14}  
15​  
16// A function which takes a closure and returns an `i32`.  
17//定义一个函数apply_to_3,其参数为闭包,返回值为i32  
18fn apply_to_3<F>(f: F) -> i32  
19where  
20   // The closure takes an `i32` and returns an `i32`.  
21   //定义这个闭包类型为Fn(不可变函数),返回一个i32值  
22   F: Fn(i32) -> i32,  
23{  
24   f(3)  
25}  
26​  
27fn main() {  
28   use std::mem;  
29​  
30   let greeting = "hello";  
31   // A non-copy type.  
32   // `to_owned` creates owned data from borrowed one  
33   //非复制类型  
34   //to_owned()方法从一个借用变量中得到数据所有权  
35   let mut farewell = "goodbye".to_owned();  
36​  
37   // Capture 2 variables: `greeting` by reference and  
38   // `farewell` by value.  
39   //闭包获取两个变量:  
40   //通过引用获取greeting  
41   //通过值 获取farewell  
42   let diary = || {  
43       // `greeting` is by reference: requires `Fn`.  
44       //greeting从引用获取值  
45       println!("I said {}.", greeting);  
46​  
47       // Mutation forces `farewell` to be captured by  
48       // mutable reference. Now requires `FnMut`.  
49       //farewell从可变引用得到数据值,所以是可修改的  
50       farewell.push_str("!!!");  
51       println!("Then I screamed {}.", farewell);  
52       println!("Now I can sleep. zzzzz");  
53​  
54       // Manually calling drop forces `farewell` to  
55       // be captured by value. Now requires `FnOnce`.  
56       //手动地释放farewell的资源  
57       mem::drop(farewell);  
58   };  
59​  
60   // Call the function which applies the closure.  
61   //把闭包diary当作一个参数传给函数apply  
62   apply(diary);  
63​  
64   // `double` satisfies `apply_to_3`'s trait bound  
65   //定义一个闭包,乘以2  
66   let double = |x| 2 * x;  
67​  
68   println!("3 doubled: {}", apply_to_3(double));  
69}  
70​
71

以上结果为:


1
2
3
4
5
6
1I said hello.  
2Then I screamed goodbye!!!.  
3Now I can sleep. zzzzz  
43 doubled: 6  
5​
6

我们看到有一个where关键词,我们这里简单介绍下where的用法 。

之前,我们再来讨论一下特征变量的绑定,如下:


1
2
3
4
5
6
7
1// Define a function `printer` that takes a generic type `T` which  
2// must implement trait `Display`.  
3//定义一个printer的函数,这个函数的参数T,必须实现特征Display  
4fn printer<T: Display>(t: T) {  
5   println!("{}", t);  
6}
7

上面就是简单的特征变量的绑定,那T也可以绑定多个特征:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1use std::fmt::{Debug, Display};  
2​  
3fn compare_prints<T: Debug + Display>(t: &T) {  
4   println!("Debug: `{:?}`", t);  
5   println!("Display: `{}`", t);  
6}  
7​  
8fn compare_types<T: Debug, U: Debug>(t: &T, u: &U) {  
9   println!("t: `{:?}`", t);  
10   println!("u: `{:?}`", u);  
11}  
12​  
13fn main() {  
14   let string = "words";  
15   let array = [1, 2, 3];  
16   let vec = vec![1, 2, 3];  
17​  
18   compare_prints(&string);  
19   //compare_prints(&array);  
20   // TODO ^ Try uncommenting this.  
21​  
22   compare_types(&array, &vec);  
23}  
24​
25

那这个多个特征绑定定义,就可以用where语句来表示,更加清晰,如下两种定义是一样的:


1
2
3
4
5
6
7
8
1impl <A: TraitB + TraitC, D: TraitE + TraitF> MyTrait<A, D> for YourType {}  
2​  
3// Expressing bounds with a `where` clause  
4//用where子语句来定义多个特征绑定定义  
5impl <A, D> MyTrait<A, D> for YourType where  
6   A: TraitB + TraitC,  
7   D: TraitE + TraitF {}
8

我们来看看例子:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1use std::fmt::Debug;  
2​  
3trait PrintInOption {  
4   fn print_in_option(self);  
5}  
6​  
7// Because we would otherwise have to express this as `T: Debug` or  
8// use another method of indirect approach, this requires a `where` clause:  
9impl<T> PrintInOption for T where  
10   Option<T>: Debug {  
11   // We want `Option<T>: Debug` as our bound because that is what's  
12   // being printed. Doing otherwise would be using the wrong bound.  
13   fn print_in_option(self) {  
14       println!("{:?}", Some(self));  
15   }  
16}  
17​  
18fn main() {  
19   let vec = vec![1, 2, 3];  
20​  
21   vec.print_in_option();  
22}  
23​
24

上面代码结果为:


1
2
1Some([1, 2, 3])
2

以上就是Rust的函数与闭包的基本知识。

以上,希望对你有用。


1
2
1如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust
2

给TA打赏
共{{data.count}}人
人已打赏
安全技术

c++编码规范

2022-1-11 12:36:11

安全运维

MySQL和MongoDB数据相互迁移

2021-12-11 11:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索