[易学易懂系列|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