rust - rust 闭包定义 insdie 一个 for 循环
问题描述
我遇到了与此问题中提到的相同的问题。简而言之,他的问题是借用一个可变对象,因为它在闭包内使用,并且由于在函数(或本例中的宏)内部使用而将其借用为不可变。
fn main() {
let mut count = 0;
let mut inc = || {
count += 2;
};
for _index in 1..5 {
inc();
println!("{}", count);
}
}
这个问题的一种解决方案是在 for 循环内而不是在 for 循环外定义闭包,或者通过使用闭包的参数传递可变引用来避免捕获变量:
1.
fn main() {
let mut count = 0;
for _index in 1..5 {
let mut inc = || {
count += 2;
};
inc();
println!("{}", count);
}
}
fn main() {
let mut count = 0;
let inc = | count: &mut i32| {
*count += 2;
};
for _index in 1..5 {
inc(&mut count);
println!("{}", count);
}
}
所以我有以下几个问题:
- 其中哪一项遵循最佳实践解决方案?
- 是否有第三种正确的做事方式?
- 根据我的理解,闭包只是匿名函数,因此多次定义它们与一次定义它们一样有效。但我无法在官方的 rust 参考资料中找到这个问题的明确答案。帮助!
解决方案
关于哪一个是正确的解决方案,我会说这取决于用例。它们是如此相似,在大多数情况下都无关紧要,除非有其他东西可以影响决定。我不知道任何第三种解决方案。
但是,闭包不仅是匿名函数,而且是匿名结构:闭包是调用匿名函数的匿名结构。结构的成员是对借用值的引用。这很重要,因为与函数不同,结构需要初始化并可能移动。这意味着您的闭包借用的值越多,初始化并作为参数传递给函数(按值)的成本就越高。同样,如果您在循环内初始化闭包,则每次迭代都可能发生初始化(如果未在循环外进行优化),使其性能不如在循环外初始化。
我们可以尝试将第一个示例脱糖为以下代码:
struct IncClusureStruct<'a> {
count: &'a mut i32,
}
fn inc_closure_fn<'a>(borrows: &mut IncClusureStruct<'a>) {
*borrows.count += 2
}
fn main() {
let mut count = 0;
for _index in 1..5 {
let mut inc_struct = IncClusureStruct { count: &mut count };
inc_closure_fn(&mut inc_struct);
println!("{}", count);
}
}
注意:编译器不一定完全这样做,但它是一个有用的近似值。
在这里您可以看到闭包结构IncClusureStruct
及其函数inc_closure_fn
,它们一起用于提供inc
. 你可以看到我们在循环中初始化了这个结构,然后立即调用它。如果我们要对第二个示例进行脱糖,IncClusureStruct
则将没有成员,但inc_closure_fn
会采用引用计数器的附加参数。然后计数器引用将转到函数调用而不是结构初始化程序。
这两个示例最终在效率方面是相同的,因为在两种情况下传递给函数的实际值的数量是相同的:1 个引用。用一个成员初始化结构与简单地初始化成员本身相同,当您到达机器代码时,包装结构就消失了。我在 Godbolt 上试过这个,据我所知,生成的程序集是一样的。但是,优化并不能涵盖所有情况。因此,如果性能很重要,那么基准测试就是要走的路。
推荐阅读
- ansible - Ansible playbook 使用集合中定义的角色
- mysql - 如何修复对共享托管 mysql 数据库的远程访问被拒绝
- node.js - Node.js 映射对象导致内存泄漏
- c# - C#如何从url下载csv文件
- swift - 如何从 Swift 中的 Struct 中的闭包中返回值?
- python - Python根据dict值获取dict键的加权平均值
- android - MoPub 横幅广告
- python - 请求失败或函数在 Scrapy 中找不到元素
- c - 如何从函数中的变量生成字符串并通过串口显示(ESP8266)
- python - 从 python 复杂的可执行字符串中删除前导零