【发布时间】:2020-01-24 00:32:47
【问题描述】:
这是我想要实现的 C++ 等价物:
std::vector<std::function<int(int)>> funcs;
funcs.emplace_back([](int n) -> int { return n + 1; });
int result = funcs[0](33);
如何用 Rust 编写上面的代码?
【问题讨论】:
标签: vector lambda rust closures
这是我想要实现的 C++ 等价物:
std::vector<std::function<int(int)>> funcs;
funcs.emplace_back([](int n) -> int { return n + 1; });
int result = funcs[0](33);
如何用 Rust 编写上面的代码?
【问题讨论】:
标签: vector lambda rust closures
如果您不打算将函数移动到任何特定位置,您可以让类型推断在代码块中为您完成工作,并从字面上定义您的闭包,就像它是一个普通变量一样(实际上,它是 - 它实现Fn 或FnMut):
let my_lambda = |n| n+1;
println!("{}", my_lambda(33));
如果您打算将此 lambda 移出堆栈,则需要将其装箱:
let my_lambda: Box<dyn Fn(u32) -> u32> = Box::new(|n| n + 1);
println!("{}", my_lambda(33));
这里的原理保持不变,唯一真正的区别是 lambda 现在在堆上。
将它们存储在Vec 中变得相对简单,因为到目前为止我们已经证明它们是“正常”类型。没有什么能阻止您创建闭包向量,但您需要将它们装箱(Vec 要求每个元素都是 Sized,并且无法通过签名来定位两个不同的闭包)并且它们需要具有相同的签名:
let my_vector: Vec<Box<dyn Fn(u16) -> u16>> = vec![
Box::new(|i| i + 1),
Box::new(|i| i - 1),
];
println!("{}", my_vector[0](33))
如果它们有不同的签名,则需要编写自己的结构来包含它们并通过签名对它们进行存储,这不是一项简单的任务。
基准案例:
C++:
#include <vector>
#include <iostream>
#include <functional>
int main(int argc, char **argv) {
std::vector<std::function<int(int)>> funcs;
for (int i = 0; i < 10000000; i++) {
funcs.push_back([&] (int n) { return n + 1; });
int result = funcs[i](33);
}
}
生锈:
fn main() {
let mut lambdas:Vec<Box<Fn(u32) -> u32>> = vec![];
for i in 0..10000000 {
lambdas.push(Box::new(|i| i+1));
lambdas[i](3);
}
}
编译选项:
-O3 --std=c++0x
--release
结果:
C++ 峰值堆大小:
--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
68 383,911,899 268,512,208 268,508,160 4,048 0
69 555,878,425 805,387,160 805,379,072 8,088 0
70 765,593,697 805,387,160 805,379,072 8,088 0
Rust 峰值堆大小:
--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
46 210,486,321 268,440,920 268,436,765 4,155 0
47 210,486,371 268,440,976 268,436,805 4,171 0
48 210,486,496 268,441,064 268,436,885 4,179 0
结论:
std::function 包装器是堆分配的。它包含的原始 lambda 本身是堆栈分配的,这种行为与 Rust 一致【讨论】:
Box 将在堆上分配每个 lambda,所以这里我们在堆上分配了向量,每个元素也在堆上分配,与 C++ 相比, lambdas 不是堆分配的(只是向量本身)。是这样吗?
Vec 本身在堆栈上;它指向内容的内部指针在堆上;并且 lambdas 本身在堆上(毕竟它们是 Vec 的元素)。
let v: Vec<fn(i32) -> i32> = vec![|n| n + 1, |n| n - 1];.