rust 闭包何时实现Fn、FnMut和FnOnce?

8ulbf1ek  于 2023-08-05  发布在  其他
关注(0)|答案(3)|浏览(118)

闭包实现FnFnMutFnOnce trait的具体条件是什么?
那就是:

  • 什么时候闭包 * 不 * 实现FnOnce trait?
  • 什么时候闭包 * 不 * 实现FnMut trait?
  • 什么时候闭包 not 实现了Fn trait?

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

t3psigkw

t3psigkw1#

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

  • FnOnceself)是可以调用一次的函数
  • FnMut&mut self)是可以在&mut环境中调用的函数
  • Fn&self)是只有对环境具有&访问权限时才能调用的函数

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

  • 所有闭包都实现FnOnce:一个不能被调用一次的闭包就不配称为闭包。注意,如果一个闭包只实现了FnOnce,那么它只能被调用一次。
  • 没有移出捕获的闭包实现了FnMut,允许它们被多次调用(如果对函数对象有无别名访问)。
  • 不需要唯一/可变访问其捕获的闭包实现了Fn,允许它们在任何地方被调用。

这些限制直接来自self的类型和闭包到结构体中的“去糖”;在我的博客文章Finding Closure in Rust中描述。
有关闭包的信息,请参阅 The Rust Programming Language 中的Closures: Anonymous Functions that Can Capture Their Environment

wgx48brx

wgx48brx2#

看到现在这里只有一个其他的答案,可能不会让一些人完全清楚问题的内容,因为它没有为我,我将提供一些例子和一些推理,帮助我理解这些闭包特性是什么:

  • 我们为什么需要他们?:“traits是函数和结构体如何指定它们可以使用和存储在其中的闭包类型(与结构体一样)”
  • 它们中的每一个都表明了一个闭包:
  • Fn
  • 不将捕获值的所有权移出其作用域(例如,移到闭包的调用方)。
  • 不捕获任何值或不改变捕获的值。
  • 因为它们不影响它们捕获的值的底层存储器的所有权(即“work with”),也不是它们捕获的值的状态,Rust允许多次调用这些闭包(因为调用它们是完全安全的操作)。
  • FnMut
  • 不将捕获值的所有权移出其范围。
  • 实际上从调用它的环境中捕获值,并且**改变这些值的状态。(“”很重要,如果没有它,那么我们将使用一个只捕获值而不以任何方式改变它们的闭包,这就是Fn trait所指示的。如手术记录:“改变闭包体的状态会使编译器无法在其上实现Fn。”)
  • FnOnce
  • 实际上将捕获值的所有权移出了它的作用域。
  • 要么不捕获任何值**,要么捕获并使用它们的方式使得多次调用此函数是不安全的(因为它可能会,例如,如果允许多次调用它-导致这些值的同一底层内存的多个所有者将其所有权移出其范围)。(“”或“”很重要:* 所有闭包都实现了这个trait,因为它们至少可以被调用一次,但是如果这个trait是它们唯一实现的trait,那么它们只能被调用一次**。

什么时候一个闭包只实现这个trait而不实现其他trait?“:当它将捕获值的所有权移出其作用域时,因为其他两个trait,根据它们的定义,如果它将捕获值的所有权移出其作用域,则不会由闭包实现。
(All这些traits只是编译器确定在哪里可以允许使用任意闭包的一种方式,同时保持闭包操作的数据的内存处于“安全”状态)

  • 一些例子:
    (note:我不能用“impl”类型注解“updated_vec”let绑定,所以我将在绑定定义附近的注解中指定一个由rust-analyzer推断的类型)
  • Fn特性:
fn main() {
    let some_vec = vec![1, 3, 4];
    
    let get_that_same_vec = || { // "get_that_same_vec" type: impl Fn() -> &Vec<i32>
        &some_vec
        // as you can see the closure is specified to implement the *Fn* trait,
        // meaning it can be called however many times since it doesn't alter the data it operates on
        // or pass the ownership of that data to any other entity 
    };
    // - By using the "&" above we are basically saying that a closure  should just return a reference to a value.
    // - If we were to omit the "&" above, we would basically be saying:
    // "return and pass the ownership of this value to whomever decides to invoke this closure"
    // which would basically be like turning it into an infinite generator of that value and its ownership
    // "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!"
    // As you can figure, Rust doesn't allow for that last sentence to be true,
    // since that would allow multiple entities to have the ownership of the underlying memory,
    // 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)   

    println!("This is the vec: {:?}", get_that_same_vec());
    println!("This is the vec: {:?}", get_that_same_vec()); // if "&" would not be present above, then this would not compile
}

字符串

  • FnMut

(On为什么我们需要用“mut”标记持有FnMut闭包的变量(参见this great answer

fn main() {
    let mut some_vec = vec![1, 3, 4];

    let mut update_vec = || { // "update_vec" type: impl FnMut()
        some_vec.push(5); 
    };

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

  • FnOnce

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

fn main() {
    let some_vec = vec![1, 3, 4];
    
    let get_that_same_vec = || { // "get_that_same_vec" type: impl FnOnce() -> Vec<i32>
        some_vec
        // as you can see the closure is specified to implement the *FnOnce* trait,
        // rust-analyzer shows only the most relevant trait that a closure implements
        // meaning that, in this case, a closure is marked as such that can only be called once,
        // since it passes the ownership of the data it captures to another entity.
        // In this case, that entity is the "get_that_same_vec" variable.
    };

    println!("This is the vec: {:?}", get_that_same_vec());
    // the call to println below does not compile and throws error "value used here after move",
    // and the way the compiler is able to infer this is by knowing
    // that a closure that implements only the `FnOnce` trait and no other trait
    // 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.
    println!("This is the vec: {:?}", get_that_same_vec()); // this does not compile
}

q43xntqr

q43xntqr3#

以下是@huon和@svitanok的回复,强调FnFnMut(以及其他类型)确实扩展了FnOnce,但它们是分开使用的:

fn main() {
    let f: Box<dyn Fn()> = Box::new(|| { println!("foo"); });
    f();
    f();
    let mut f: Box<dyn FnMut()> = Box::new(|| { println!("foo"); });
    f();
    f();
    let f: Box<dyn FnOnce()> = Box::new(|| { println!("foo"); });
    f();
    f(); // compile error!
}

字符串

相关问题