我喜欢使用部分应用程序,因为它允许(除其他外)拆分复杂的函数调用,这更具可读性。
部分应用示例:
fn add(x: i32, y: i32) -> i32 {
x + y
}
fn main() {
let add7 = |x| add(7, x);
println!("{}", add7(35));
}
Run Code Online (Sandbox Code Playgroud)
这种做法有开销吗?
这是我喜欢做的事情(来自真实的代码):
fn foo(n: u32, things: Vec<Things>) {
let create_new_multiplier = |thing| ThingMultiplier::new(thing, n); // ThingMultiplier is an Iterator
let new_things = things.clone().into_iter().flat_map(create_new_multiplier);
things.extend(new_things);
}
Run Code Online (Sandbox Code Playgroud)
这纯粹是视觉上的。我不喜欢铺满太多的东西。
在您的特定示例中,是的,extend 可以作为循环内联,其中包含另一个循环 for ,flat_map而后者只是将ThingMultiplier实例放入相同的堆栈槽中,n并持有and thing。
但是你在这里吠叫错误的效率树。与其想知道一个包含两个字段的小结构的分配是否被优化掉了,不如想知道它的效率有多高clone,尤其是对于大输入。
在使用之前定义闭包与直接定义和使用它之间不应该有性能差异。有一个类型系统差异——编译器不完全知道如何在没有立即调用的闭包中推断类型。
在代码中:
let create_new_multiplier = |thing| ThingMultiplier::new(thing, n);
things.clone().into_iter().flat_map(create_new_multiplier)
Run Code Online (Sandbox Code Playgroud)
将与完全相同
things.clone().into_iter().flat_map(|thing| {
ThingMultiplier::new(thing, n)
})
Run Code Online (Sandbox Code Playgroud)
一般来说,使用闭包不应该有性能成本。这就是 Rust 所说的“零成本抽象”的意思:程序员自己写得再好不过了。
编译器将闭包转换Fn*为匿名结构上特征的实现。那时,所有正常的编译器优化都开始了。由于像单态化这样的技术,它甚至可能更快。这确实意味着您需要进行正常的分析以查看它们是否是瓶颈。