'static reference does not live long enough

Apologies for the slightly convoluted example; it was reduced from a much longer program.

I'm building a 'static shared reference which can be used anywhere in an async program; using Box::leak should make that work, since the original value is just a struct which carries no lifetime parameters.
Here is what I do:

use std::future::Future;

struct Foo {
    baz: String,
}

fn blip(foo: &'static Foo) {
    println!("It's a {}", foo.baz);
}

fn yargh<F>(bzink: F) where F: Future + 'static {
    
}

#[tokio::main]
async fn main() {
    let baz = "bla".to_owned();
    let foo: &'static Foo = Box::leak(Box::new(Foo { baz }));
    let fut = async { blip(foo) };
    yargh(fut);
}

(Playground)

Yet I am getting unexpected compiler errors::

   Compiling playground v0.0.1 (/playground)
warning: unused variable: `bzink`
  --> src/lib.rs:11:13
   |
11 | fn yargh<F>(bzink: F) where F: Future + 'static {
   |             ^^^^^ help: consider prefixing with an underscore: `_bzink`
   |
   = note: `#[warn(unused_variables)]` on by default

error[E0597]: `foo` does not live long enough
  --> src/lib.rs:19:28
   |
19 |     let fut = async { blip(foo) };
   |               -------------^^^---
   |               |     |      |
   |               |     |      borrowed value does not live long enough
   |               |     value captured here by generator
   |               argument requires that `foo` is borrowed for `'static`
20 |     yargh(fut);
21 | }
   | - `foo` dropped here while still borrowed

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

Am I in the wrong, or is the compiler?
How else do I conjure convenient &'static refs?

You need to use a async move block to capture the environment by value instead of by reference.

#[tokio::main]
async fn main() {
    let baz = "bla".to_owned();
    let foo: &'static Foo = Box::leak(Box::new(Foo { baz }));
-   let fut = async { blip(foo) };
+   let fut = async move { blip(foo) };
    yargh(fut);
}

Thank you, that makes it work.
I do not understand why foo, a reference, would not be treated as Copy and captured safely by default.

Async blocks and closures never capture by value without the move keyword.

Just as a tidbit, capture by move is inferred per capture when the value is consumed. For example:

fn main() {
    let s = String::from("hello");
    let f = || drop(s);
    println!("{:?}", f());
}

(This example compiles, the string is consumed, and it outputs () which is the return value from drop.)

Also notice that nightly will give you this solution already.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.