Hacker News new | past | comments | ask | show | jobs | submit login

I am not a Rust user and maybe I am reading the post wrong but it seems that syntax has been deprecated:

> Closures: Rust now supports full capture-clause inference and has deprecated the temporary |:| notation, making closures much more ergonomic to use.




That's talking about something different.

For a brief period, closures had to be annotated in certain cases like |&: args| or |&mut: args| or |: args| to determine whether they captured their environment by (mutable) reference or by value/move.

Now that this is inferred in all cases, closure arguments can just be written as |args| in all cases, just as they were before the current Fn* traits were introduced.


> For a brief period, closures had to be annotated in certain cases like |&: args| or |&mut: args| or |: args| to determine whether they captured their environment by (mutable) reference or by value/move.

That particular annotation controlled the access a closure has to its environment, not how it's captured. |&:|, |&mut: |, and |:| corresponded to the Fn, FnMut, and FnOnce traits, respectively. If you look at the signatures of those traits, you'll see that Fn's method takes self by reference, FnMut takes it by mutable reference, and FnOnce takes it by value. In particular, this means that the body of an FnOnce closure can move values out from the closure (that's why it can only be called once), whereas Fn and FnMut can only access values in the closure by reference and mutable reference, respectively.

The way variables are captured from the environment into the closure is controlled by the "move" keyword. If the "move" keyword precedes a closure expression, then variables from the environment are moved into the closure, which takes ownership of them. "move" is usually associated with FnOnce closures, but it's also needed when returning a boxed Fn or FnMut closure from a function, as you can see below:

    fn make_appender(x: String) -> Box<Fn(&str) -> String + 'static> {
        Box::new(move |y|
            // The closure has & access to its captured variable, but
            // it has been moved into the closure so it outlives the
            // body of make_appender, thanks to the move
            // keyword.
            x.clone() + y
        )
    }

    fn main() {
        let x = "foo".to_string();
        let appender = make_appender(x);
        println!("{} {}", appender("bar"), appender("baz"));
    }


> The way variables are captured from the environment into the closure is controlled by the "move" keyword. If the "move" keyword precedes a closure expression, then variables from the environment are moved into the closure, which takes ownership of them.

Note: if you don't specify `move`, then the captures are determined in the usual way:

`|| v.len()` captures `v` via an immutable borrow

`|| v.push(0)` captures `v` via a mutable borrow

`|| v.into_iter()` captures `v` by moving it


Thanks for the correction.


Fortunately with the annotations gone, I think there will be a lot less confusion! The "move" keyword on its own is pretty straightforward, at least once you've learned Rust's ownership model.


I wish they also had the option of a pure lambda that isn't a closure. You can't pass a pure lambda to an argument expecting an ordinary function.


What was deprecated is the explicit closure type specification, which is only the ":" part.


Right. IIRC, now your choices are, roughly:

    |args| expr // upvars captured by reference, can't be called after function has gone out of scope

    move |args| expr // upvars moved from function to the closure context (or copied if trivially copyable)
This is simple and good enough for most use cases. If you want more complex schemes, you have to implement them manually, e.g. to reference count the upvars, like Apple blocks do by default, wrap them in Rc and capture that.

Accepting closures is a bit more complicated though.


That's not quite right. In the non-move case, the capture is inferred per upvar. See my other comment for details (https://news.ycombinator.com/item?id=9047766)




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: