将 C++ std::function 存储在向量中的 Rust 等价物是什么?

Rod*_*igo 2 lambda closures vector rust

这是我试图实现的 C++ 等价物:

std::vector<std::function<int(int)>> funcs;
funcs.emplace_back([](int n) -> int { return n + 1; });
int result = funcs[0](33);
Run Code Online (Sandbox Code Playgroud)

如何在 Rust 中编写上面的代码?

Séb*_*uld 6

如果您不打算将函数移动到特定的任何地方,您可以让类型推断在代码块中为您完成工作,并且非常严格地定义您的闭包,就像它是一个普通变量(实际上,它是 - 它实现了FnFnMut):

let my_lambda = |n| n+1;
println!("{}", my_lambda(33));
Run Code Online (Sandbox Code Playgroud)

操场

如果您打算将此 lambda 移出堆栈,则需要将其装箱:

let my_lambda: Box<dyn Fn(u32) -> u32> = Box::new(|n| n + 1);
println!("{}", my_lambda(33));
Run Code Online (Sandbox Code Playgroud)

操场

这里的原则保持不变,唯一真正的区别是 lambda 现在在堆上。

将它们存储在Vecthen 中变得相对简单,因为现在我们已经证明它们是“正常”类型。没有什么可以阻止您创建闭包向量,但是您需要将它们装箱(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))
Run Code Online (Sandbox Code Playgroud)

如果它们具有不同的签名,您将需要编写自己的结构来包含它们并通过签名对它们进行存储,这不是一项简单的任务。


基准案例:

编译选项:

  • G++: -O3 --std=c++0x
  • 货物: --release

结果:

结论:

  • C++std::function包装器是堆分配的。它包含的原始 lambda 本身是堆栈分配的,这种行为与 Rust 一致
  • Rust 更擅长内存优化,占用 C++ 三分之一的堆大小
  • Rust 在原始时间方面也更快

  • “但你需要把它们装箱”——这通常是不正确的。如果闭包不捕获任何局部变量,则可以将它们强制转换为函数指针,并且可以将它们直接存储在向量中: `let v: Vec&lt;fn(i32) -&gt; i32&gt; = vec![|n| n + 1,|n| n - 1];`。 (6认同)