本文主要是介绍Rust语言入门教程(十四) - 闭包Closure,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
什么是闭包
闭包在 Rust 中是非常强大的功能,允许你编写更灵活和表达性的代码。闭包的语法和功能在某些方面类似于其他语言(如 JavaScript 或 Python)中的匿名函数或 lambda 表达式。
在Rust中,当我们想要生成一个新的线程,或者是想在迭代中进行一些函数式编程对迭代变量进行处理时,通常就会用到闭包, 当然,在标准库中一些其他的常见的地方,也会涉及到闭包的使用。
闭包本质上是一个匿名函数,它可以从其所在的作用域中借用或捕获到一些变量或数据。
语法规范
|x, y| { x+y }
上面的代码展示了一个闭包的基本语法格式:
- 在两个
|
之间是闭包的参数列表,参数之间用,
分隔; - 参数不需要标注数据类型;
- 竖线后面是一个匿名函数的代码块,可以用于代码调用;
其中,参数的类型和和匿名函数的返回值都是由编译器通过代码的上下文自动推断得出。让我们来看一个实际的例子:
let add = |x, y| { x + y };
add(1, 2); // return 3
上面的代码中,我们将闭包分配给了一个变量add
, 然后通过传入参数1, 2
调用这个闭包,这两个变量将被传入匿名函数执行,并返回3
。
捕获变量
闭包的参数列表可以为空,因此上面的例子可以是这样:
|| { x + y }
当然,匿名函数也可以为空,比如这样:
|| {}
但是显然这样做没什么意义,也没人会干这么无聊的事情。那么为什么参数列表可以为空呢,那匿名函数中的变量从何而来呢? 这就到了有意思的地方了, 闭包可以借用它被定义的作用域中的值的引用。
let s = "abc".to_string();
let f = || {println!("{}", s);
};f() // 输出 “abc”
在上面的代码中,我们创建了一个字符串变量s
, 然后创建了一个闭包并分配给变量f
, 其中的匿名函数借用了该作用域中变量s
的引用(因为println!
是使用的参数的引用), 从而匿名函数本身不需要参数列表。这样一来,我们每次通过变量f
调用这个匿名函数时,都可以打印出abc
。
所有权移动 - move
但是这存在一个问题,匿名函数中只是借用了变量s
的引用,如果变量s
一旦超出作用域而被销毁, f
中的引用就成为了一个悬空指针,这是Rust编译器所不允许的。
为了解决这个问题, 闭包也支持使用move
关键字,来强制的将闭包中使用的任何变量移动到自身的作用域中并获取他们的所有权,而不再只是借用。
let s = "abc".to_string();
let f = move || {println!("{}", s);
};f();
这样一来s
的所有权便被闭包拥有,它将一直存在,直至闭包本身超出作用域被销毁为止。我们可以把这个闭包传递给另一个线程,或者作为函数的返回值返回,等等。
在迭代器中使用闭包
如果想在迭代器中进行一些函数式的编程,对迭代变量进行处理,闭包也是真香。
let mut v = vec![2, 4, 6];
v.iter().map(|x| x * 3).filter(|x| *x > 10).fold(0, |acc, x| acc + x)
上面的代码中,对一个向量调用iter()
函数,获得了一个迭代器,将对这个向量中的每个值进行迭代。然后就可以多种多样的使用到闭包的方法了。
map()
: 对向量中每个项都乘以3;filter()
: 丢弃向量中不大于10的项;fold()
: 将筛选后的向量中的所有的值相加求和;
小结
本章介绍了Rust中闭包的概念和使用场景,已经如何将变量的所有权转移到闭包中。下一章我们将讨论Rust的多线程。
这篇关于Rust语言入门教程(十四) - 闭包Closure的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!