Ano*_*non 0 struct closures rust
我在理解闭包方面遇到了一些困难,所以我跳到一个论坛上询问一些有关幕后情况的问题。有人给我举了这样一个例子:
对于以下代码:
Run Code Online (Sandbox Code Playgroud)let x = String::new(); let f = || { println!("{x}") }; f();下面的代码是 Rust 在后台生成的(这只是一个近似值,它并没有真正运行):
Run Code Online (Sandbox Code Playgroud)struct UnnameableClosureType<'a> { x0: &'a String, } impl<'a> Fn<()> for UnnameableClosureType<'a> { type Output = (); extern "rust-call" fn call(&self, args: Args) -> Self::Output { println!("{}", self.x0); } } impl<'a> FnMut<()> for UnnameableClosureType<'a> { type Output = (); extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output { self.call(args) } } impl<'a> FnOnce<()> for UnnameableClosureType<'a> { type Output = (); extern "rust-call" fn call_once(mut self, args: Args) -> Self::Output { self.call_mut(args) } } let x = String::new(); let f = UnnameableClosureType { x0: &x }; f();由于这需要在调用时消耗 x,因此唯一可以实现的特征是 FnOnce,而不是 Fn 或 FnMut。
这就是我得到的解释。
我真正不明白的是编译器如何知道 FnOnce 是唯一可以实现的特征。
我尝试建议,如果输入变量(这是一个移动值)被移动到&self.x0然后被删除,就会有一个悬空引用&self.x0,但我不确定这是否正确,即使这对我来说听起来有点奇怪因为这描述了我们想要避免的结果(即使如此,我也不确定这是否真的会发生)而不是编译器使用的规则。
我得到的解释是否正确,我的理解是否合理?
我真正不明白的是编译器如何知道 FnOnce 是唯一可以实现的特征。
那是因为事实并非如此。解释是错误的;给定的闭包实现了所有三个;Fn,FnMut和FnOnce。
我不确定编译器生成的确切代码,但其背后的原理如下:
Fn,FnMut和FnOnce,除非有什么阻止。例子:
// Fn + FnMut + FnOnce
let f = |x| 2*x;
Run Code Online (Sandbox Code Playgroud)
&mut,它将不再执行Fn,因为Fn只能执行不可变的操作。例子:
let mut sum = 0;
// FnMut + FnOnce
let f = |x| sum += x;
Run Code Online (Sandbox Code Playgroud)
FnMut或Fn,因为这两者都不允许消费东西。例子:
let s = String::new();
// Only FnOnce, because `drop` consumes `s`
let f = || drop(s);
Run Code Online (Sandbox Code Playgroud)
此外,请注意,是否存在闭包move并不影响它实现哪些特征。决定这些特征的是它如何使用这些捕获 - 例如,如果它调用&mut self此类捕获对象的函数,则会删除该Fn特征,因为Fn闭包不能改变事物。