闭包何时实现Fn,FnMut和FnOnce?

Den*_*rim 97 closures rust

哪些具体条件为闭合来实现Fn,FnMutFnOnce特质?

那是:

  • 闭包什么时候没有实现这个FnOnce特性?
  • 闭包什么时候没有实现这个FnMut特性?
  • 闭包什么时候没有实现这个Fn特性?

例如,改变它的主体上的闭包状态会使编译器无法实现Fn它.

huo*_*uon 104

每个特征代表关于闭包/函数的越来越多的限制性属性,由它们的call_...方法的签名表示,特别是self:

  • FnOnce(self)是可以调用一次的函数,
  • FnMut(&mut self)是可以在有权&mut访问其环境时调用的函数
  • Fn(&self)是只有在只能&访问其环境时仍可以调用的函数.

闭包|...| ...将自动实现尽可能多的闭包.

  • 所有闭包实现FnOnce:一个不能被调用的闭包不值得这个名字.请注意,如果只实现闭包FnOnce,则只能调用一次.
  • 不会移出其捕获的闭包实现FnMut,允许多次调用它们(如果对函数对象没有通用的访问).
  • 不需要对其捕获实现唯一/可变访问的闭包实现Fn,允许它们在任何地方被调用.

这些限制直接self取决于封闭物到结构的类型和"desugaring"(在Rust查找封闭中描述).

有关Rust中截止日期的闭包的信息,请参阅Rust书中的Closures章节.

  • 我误读了纳尔普利的评论,这让我有些困惑.未来的读者,请注意他说"如果封闭*只*实现'FnOnce`". (6认同)
  • 有几种思考方法: 1. `FnOnce` 中的“Once”指的是调用者调用它的次数的上限,而不是它_可以_被调用的次数。2. 您始终可以将可多次调用的闭包转换为只能调用一次的闭包:只需在第一次调用后丢弃与闭包关联的任何内存即可。但你不能以其他方式将其转换回来。 (4认同)
  • 实现细节:*将自动实现尽可能多的实现。*并非完全正确,如果需要,它将自动实现。您可以使用专门化检测用于FnMut参数的闭包缺少的Fn-impl。这是错误https://github.com/rust-lang/rust/issues/26085 (2认同)
  • 我仍然很难理解所有闭包如何实现“FnOnce”,即使它们可以被多次调用。这种特征的名称令人困惑。 (2认同)

end*_*ell 8

鉴于现在只有一个其他答案可能无法让某些人完全清楚问题的内容,因为它对我来说并不完全清楚,我将提供一些示例以及一些推理来帮助我理解这些内容封闭特征都是关于:

\n

\xe2\x80\x94 为什么我们需要它们?:“特征是函数和结构如何指定它们可以使用和存储在其中的闭包类型(与结构一样)”

\n

\xe2\x80\x94 它们各自对于闭包表示什么:

\n
    \n
  • Fn

    \n
      \n
    • 不会将捕获值的所有权移出其范围(例如,移至闭包的调用者)。
    • \n
    • 要么不捕获任何值,要么不改变捕获的值。
    • \n
    • 因为它们不影响它们捕获的值的底层内存的所有权(即“使用”),也不影响它们捕获的值的状态,Rust 允许多次调用这些闭包(因为调用它们是完全安全的操作) 。
    • \n
    \n
  • \n
  • FnMut

    \n
      \n
    • 不会将捕获值的所有权移出其范围。
    • \n
    • 实际上从调用它的环境中捕获值改变这些值的状态。(“”很重要,如果不是它,那么我们将使用一个仅捕获值并且不会以任何方式改变它们的闭包,这就是Fn用于指示的特征。 op 指出:“改变其主体上的闭包状态会使编译器无法Fn在其上实现。”)
    • \n
    \n
  • \n
  • FnOnce

    \n
      \n
    • 实际上会将捕获值的所有权移出其范围。
    • \n
    • 要么不捕获任何值,要么捕获并以多次调用此函数不安全的方式使用它们(因为,例如,如果允许多次调用它,可能会导致多个所有者这些值的相同底层内存将其所有权移出其范围)。(“”很重要:所有闭包都实现了这个特征,因为它们至少可以被调用一次,但是如果这个特征是它们实现的唯一特征而没有其他,那么它们只能被调用一次)\n

      ^^ ”闭包何时仅实现此特征而不实现其他特征?”:当它将捕获值的所有权移出其范围时,因为根据其他两个特征的定义,如果闭包移动了所有权,则闭包不会实现捕获的值超出其范围。
    • \n
    \n
  • \n
\n

(所有这些特征只是编译器确定在何处允许使用任意闭包同时将闭包操作的数据内存保持在“安全”状态的一种方法)

\n

\xe2\x80\x94 一些示例:

\n

(注意:我无法用“impl”类型注释“updated_vec”let 绑定,因此我将在绑定定义附近的注释中指定 rust 分析器推断的类型)

\n
    \n
  • Fn特征:
  • \n
\n
fn main() {\n    let some_vec = vec![1, 3, 4];\n    \n    let get_that_same_vec = || { // "get_that_same_vec" type: impl Fn() -> &Vec<i32>\n        &some_vec\n        // as you can see the closure is specified to implement the *Fn* trait,\n        // meaning it can be called however many times since it doesn\'t alter the data it operates on\n        // or pass the ownership of that data to any other entity \n    };\n    // - By using the "&" above we are basically saying that a closure  should just return a reference to a value.\n    // - If we were to omit the "&" above, we would basically be saying:\n    // "return and pass the ownership of this value to whomever decides to invoke this closure"\n    // which would basically be like turning it into an infinite generator of that value and its ownership\n    // "Oh, another caller wants this value and the ownership of it? Sure, let me take the copy of that value... out of thin air I guess!"\n    // As you can figure, Rust doesn\'t allow for that last sentence to be true,\n    // since that would allow multiple entities to have the ownership of the underlying memory,\n    // which would eventually result in a "double free" error when needing to free that underlying memory when one of the owners goes out of scope. (see: *FnOnce* example)   \n\n    println!("This is the vec: {:?}", get_that_same_vec());\n    println!("This is the vec: {:?}", get_that_same_vec()); // if "&" would not be present above, then this would not compile\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • FnMut
  • \n
\n

FnMut(关于为什么我们需要用“mut”标记持有闭包的变量,请参阅这个很好的答案

\n
fn main() {\n    let mut some_vec = vec![1, 3, 4];\n\n    let mut update_vec = || { // "update_vec" type: impl FnMut()\n        some_vec.push(5); \n    };\n\n    // As you can see the closures that implement the *FnMut* trait can be called multiple times,\n    // because they do not pass the ownership of the data they capture out of their scope\n    // they only alter its state, and if altering the value of its state multiple times is a legal operation\n    // for a type on which the closure operates, then it is surely ok to call such a closure multiple times  \n    update_vec();\n    update_vec();\n    println!("This is the updated \\"some_vec\\": {:?}", some_vec);\n    // This is the updated "some_vec": [1, 3, 4, 5, 5]\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • FnOnce
  • \n
\n

(我在这里所做的就是:我删除了示例中闭包内“some_vec”前面的“&” Fn

\n
fn main() {\n    let some_vec = vec![1, 3, 4];\n    \n    let get_that_same_vec = || { // "get_that_same_vec" type: impl FnOnce() -> Vec<i32>\n        some_vec\n        // as you can see the closure is specified to implement the *FnOnce* trait,\n        // rust-analyzer shows only the most relevant trait that a closure implements\n        // meaning that, in this case, a closure is marked as such that can only be called once,\n        // since it passes the ownership of the data it captures to another entity.\n        // In this case, that entity is the "get_that_same_vec" variable.\n    };\n\n    println!("This is the vec: {:?}", get_that_same_vec());\n    // the call to println below does not compile and throws error "value used here after move",\n    // and the way the compiler is able to infer this is by knowing\n    // that a closure that implements only the `FnOnce` trait and no other trait\n    // can only be called once, it no longer holds the ownership of a value it moved the ownership of the first time it was called.\n    println!("This is the vec: {:?}", get_that_same_vec()); // this does not compile\n}\n
Run Code Online (Sandbox Code Playgroud)\n