Search code examples
ruststaticclosures

What does a closure prefixed with `static` mean? And when would I use it?


I stumbled across an example with static || { } in docs which works. Yet an attempt to use such an expression in my own code fails. I wonder why.

the very example from https://doc.rust-lang.org/stable/std/pin/macro.pin.html

#![feature(generators, generator_trait)]
use std::{
    ops::{Generator, GeneratorState},
    pin::pin,
};

fn generator_fn() -> impl Generator<Yield = usize, Return = ()> /* not Unpin */ {
 // Allow generator to be self-referential (not `Unpin`)
 // vvvvvv        so that locals can cross yield points.
    static || {
        let foo = String::from("foo");
        let foo_ref = &foo; // ------+
        yield 0;                  // | <- crosses yield point!
        println!("{foo_ref}"); // <--+
        yield foo.len();
    }
}

fn main() {
    let mut generator = pin!(generator_fn());
    match generator.as_mut().resume(()) {
        GeneratorState::Yielded(0) => {},
        _ => unreachable!(),
    }
    match generator.as_mut().resume(()) {
        GeneratorState::Yielded(3) => {},
        _ => unreachable!(),
    }
    match generator.resume(()) {
        GeneratorState::Yielded(_) => unreachable!(),
        GeneratorState::Complete(()) => {},
    }
}

my attempt which fails:

fn func() -> impl Fn() {
    static || {
        println!("qwerty");
    }
}

with the following error:

error[E0697]: closures cannot be static
 --> ./ex_099.rs:2:5
  |
2 |     static || {
  |     ^^^^^^^^^

rust version:

$ rustc --version
rustc 1.70.0-nightly (88fb1b922 2023-04-10)

Solution

  • At the time of writing, the Rust stable toolchain does not support static closures in any way. What you are seeing is the use of the unstable feature generators, which resorts to a closure-like syntax to represent generator functions. Quoting a few relevant parts:

    A generator is a "resumable function" that syntactically resembles a closure but compiles to much different semantics in the compiler itself. [...] Generators use the yield keyword to "return", and then the caller can resume a generator to resume execution just after the yield keyword.

    [...]

    Generators are closure-like literals which can contain a yield statement. The yield statement takes an optional expression of a value to yield out of the generator. All generator literals implement the Generator trait in the std::ops module

    As an unstable feature, any details about its functioning are subject to change. As of nightly-2023-04-02, the syntax static || { /* ... */ } will only work if you add the necessary feature attributes, make the function return impl Generator instead of impl Fn, and you include at least one yield in the generator.

    #![feature(generators, generator_trait)]
    use std::ops::Generator;
    
    fn func() -> impl Generator<Yield = (), Return = ()> {
        static || {
            yield;
            println!("qwerty");
        }
    }
    

    Playground

    See also: