为什么Rust将闭包捕获的i64存储为LLVM IR闭包环境中的i64*s?

为什么Rust将闭包捕获的i64存储为LLVM IR闭包环境中的i64*s?,rust,Rust,在这个简单的例子中 #[inline(never)] fn apply<F, A, B>(f: F, x: A) -> B where F: FnOnce(A) -> B { f(x) } fn main() { let y: i64 = 1; let z: i64 = 2; let f = |x: i64| x + y + z; print!("{}", apply(f, 42)); } (apply实际上在生成的LL

在这个简单的例子中

#[inline(never)]
fn apply<F, A, B>(f: F, x: A) -> B
    where F: FnOnce(A) -> B {
    f(x)
}

fn main() {
    let y: i64 = 1;
    let z: i64 = 2;
    let f = |x: i64| x + y + z;
    print!("{}", apply(f, 42));
}
apply
实际上在生成的LLVM代码中有一个更复杂的名称。)

这将导致两个加载到达每个捕获的变量。为什么
%closure
不只是
{i64,i64}
(这将使
的参数适用于
{i64,i64}*
)?

默认情况下通过引用捕获。通过在参数列表之前添加
move
关键字,可以将该行为更改为按值捕获:

let f = move |x: i64| x + y + z;
这将生成更精简的代码:

define internal fastcc i64 @apply(i64 %.0.0.val, i64 %.0.1.val) unnamed_addr #0 personality i32 (i32, i32, i64, %"8.unwind::libunwind::_Unwind_Exception"*, %"8.unwind::libunwind::_Unwind_Context"*)* @rust_eh_personality {
entry-block:
  %0 = add i64 %.0.0.val, 42
  %1 = add i64 %0, %.0.1.val
  ret i64 %1
}
添加
move
关键字意味着闭包使用的任何值都将被移动到闭包的环境中。对于整数(即
Copy
)来说,这没有多大区别,但是对于其他类型(如
String
)来说,这意味着在创建闭包后,您不能在外部范围内使用
字符串。这是一个要么全有要么全无的交易,但您可以手动引用
移动
闭包之外的单个变量,并让闭包使用这些引用,而不是原始值,以获得手动捕获引用行为

你能在这段代码中观察到数值与参考值的差异吗


请注意,第一行和第二行的输出是相同的,第三行是不同的。

谢谢您的解释。我假设,既然复制
i64
s和引用它们一样便宜(如果不便宜的话),而且既然不能变异
i64
s,那么crust只会透明地按值复制它们。你能在这段代码中观察到数值与参考值的差异吗?
define internal fastcc i64 @apply(i64 %.0.0.val, i64 %.0.1.val) unnamed_addr #0 personality i32 (i32, i32, i64, %"8.unwind::libunwind::_Unwind_Exception"*, %"8.unwind::libunwind::_Unwind_Context"*)* @rust_eh_personality {
entry-block:
  %0 = add i64 %.0.0.val, 42
  %1 = add i64 %0, %.0.1.val
  ret i64 %1
}