rust十三.1、匿名函数(闭包)
在编译后,所谓的闭包是编译为单独的函数,以是原文的作者也把closure称为函数。因此,本文也称为函数。这个更好理解的一个概念。
一、概念
在某个程序体内定义的一段代码,该代码不具有fn的格式,但是实现函数的作用,如许的代码称为匿名函数(closure)。
匿名函数这个东西,现在各个语言大行其道,焦点的原因是更加方便,某些风俗如许思维的工程师能从此受益。
在没有匿名函数之前,程序也运行得好好的。
以是,所谓的匿名函数,本质上是编译器的把戏!
二、定义方式
主要有两种方式:
[*]指定持有者变量方式
[*]无持有者变量方式-这一种相当常见
2.1、指定持有者变量方式
let f = |x| { println!("不改变捕获的x={}", x) };2.2、无持有者变量方式
fn giveaway(&mut self, user_prefer:Option) -> ShirtColor { user_prefer.unwrap_or_else(|| self.most_stocked())}函数unwrap_or_else中内容就是一个匿名函数. 2.3、和别的语言比较
前文说过,现在很多语言都有这种图方便的写法。java有匿名函数和朗达表达式,javascript也有类似的匿名函数和朗达表达式。和rust比起来,个人觉得还是java,js的誊写方式更加地人性化。例如java可以如许写:Isort sort2 = (a, b) -> a + b;
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Hello from anonymous Runnable!");
}
};
js可以如许写
// 带有对象字面量的箭头函数(注意:需要括号)
const createPerson = (name, age) => ({ name: name, age: age });
const person = createPerson("Alice", 30);
console.log(person); // 输出: { name: 'Alice', age: 30 }
// 箭头函数中的 this 绑定
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // `this` 绑定到 Person 实例
console.log(this.age);
}, 1000);
}rust利用的是比较怪异的 ||替代(),而且参数定义区域和函数体之间只是用空格分隔,这种方式无疑会让初学者误会,也不太符合大部分语言的约定。
然而,刻意地与众差别,应该是rust发明人追求目标。
只要如许这个目标没有影响到别的一个目标(安全、高效),那么也还是可以忍受的!
三、变量捕获和引用问题
如果有学过别的语言,这个其实很容易理解,其实么有特别好理解的。
rust的麻烦主要是所有权和引用所导致的。
这些问题其实可以归结为三个:
[*]如何捕获
[*]影响之-如何利用:能不能修改
[*]影响之-所有权
3.1、捕获方式
一句话:主动捕获
编译器通过匿名函数的编写和实验时候所利用的参数来确定捕获了什么变量。
直接在函数体捕获外部变量
let mut z = 20;let mut closure2 = || { z += 1;};通过参数捕获(实为传参)
let fx=|y| y+1;let v=20;let r=fx(v);println!("{}+1={}",v,r); 处于图方便的缘故,第一种形式比较多,这一点在js中也是类似的。3.2、可变捕获
即不但捕获,还要在匿名函数体内改变被捕获变量的值。
如果采用”指定持有者变量“方式来定义此类匿名函数,那么必须把为这个变量指定mut关键字,例如:
let mut z = 20;let mut closure2 = || { z += 1;};如果不是,则不需要。 可变捕获后,有一个很特别的事情需要记住:一旦你利用了可变捕获捕获一个变量,那么在最后一次匿名函数被调用之前,你不能在父级作用域利用被捕获的变量。否则,编译器会提示:immutable borrow occurs here.或者提示 :cannot borrow `xxx` as mutable more than once at a time 3.3、所有权问题
在默认情况下,捕获变量,不会导致所有权厘革,只是纯粹的引用:不可变引用,或者是可变引用。
大部分时候,我们只是希望借用下,就像大部分语言,函数那样利用匿名函数。匿名还是在这种情况下,仅仅只是作为一个黑盒,有借有还!
如果你希望所有权转移到匿名函数体内,那么需要借助关键字 move,例如:
let mut move_fn= move || { vec.push(4); println!("{:?}", vec);};以上一段代码中,匿名函数closure3拥有了vec的所有权。
当然奇特的不但仅是在于这,而是你如果实验了类似move_fn一次后,还可以持续多次调用move_fn,而且vec的值会不停厘革。
基于别的语言的概念和风俗,工程师们需要一段时间来适应这种有别于传统的方式。然而难度也不是那么大,只是有一点而已。风俗它,只要知道编译器就是这么规定的,困难的事情是编译器在做。
四、示例
fn main() {
let x = 10;
// 1. 没有捕获
let closure1 = |y| x + y;
println!("1.0执行前x={}", x); //x是不可变引用,所以x可以在父级作用域不停使用
let x1=closure1(10);
println!("1.0现在{}+{}={},x还是{}", x,10,x1,x); //x是不可变引用,所以x可以在父级作用域不停使用
// 1.5 捕获,但是不变
let f = |x| { println!("不改变捕获的x={}", x) };
println!("1.5执行前x={}", x); //x是不可变引用,所以x可以在父级作用域不停使用
f(x);
println!("1.5现在x={}", x); //x是不可变引用,所以x可以在父级作用域不停使用
//2.0 可变捕获
let mut z = 20;
println!("2.0执行前,z={}", z);
// 可变捕获,实现FnMut
let mut closure2 = || {
z += 1;
};
closure2();
//println!("第一次调用后,z={}", z); //编译错误 immutable borrow occurs here
closure2();
println!("2.0第二次调用后,z={}", z);
// 3.0 所有权转移的可变捕获
let mut vec = vec!;
println!("3.0执行前,vec={:?}", vec);
// 所有权转移,只实现FnOnce
let mut closure3 = move || {
vec.push(4);
println!("{:?}", vec);
};
closure3();
//println!("现在vec={:?}", vec);//编译错误,因为被closure3借用后,vec已经不在范围之内(消失了);
closure3();
letfx=|y| y+1;
let v=20;
let r=fx(v);
println!("{}+1={}",v,r);
}
五、小结
[*]匿名函数的确方便了新一代的工程师。但匿名函数在rust还是有大用的
[*]rust的所有权问题让它的匿名函数和别的语言存在较大的差别
[*]如果是可变捕获,那么会存在一个糟糕的,比较难于理解的现象:一旦你利用了可变捕获捕获一个变量,那么在最后一次匿名函数被调用之前,你不能在父级作用域利用被捕获的变量。
而在别的语言中,不会有这个问题:因为我们都认为:定义是定义,都还有利用怎么就捕获了?
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]