Closure that expects closure forces it to be 'static

In this simple example, I took the care of making sure DecoderProvider is a function that expects a reference to a closure, so it expects &Box<dyn Fn(&Option<&Arc<u8>>)>. This way, it uses the closure very shortly, and thus do not require the closure to be borrowed for 'static. But below, it looks like calling decoder_provider(&provide_decoder); forces provide_decoder to live longer than fn main, which contains on_packet_render. Why?

use std::sync::Arc;
pub type DecoderProvider = Arc<dyn Fn(&Box<dyn Fn(&Option<&Arc<u8>>)>)>;

fn main() {
    let on_packet_render =
        Arc::new(|packet: Option<Box<u8>>| {
            //render packet here
        });
    let decoder = Arc::new(0);
    let decoder_provider:DecoderProvider = Arc::new(
        move |b: &Box<dyn Fn(&Option<&Arc<u8>>)>| {
            b(&Some(&decoder));
        },
    );
    let provide_decoder: Box<dyn Fn(&Option<&Arc<u8>>)> = 
        Box::new(|decoder| {
            on_packet_render(None);
        });
    
    decoder_provider(&provide_decoder);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0016a84b46a1eaee96e065596ec07243

error[E0597]: `on_packet_render` does not live long enough
  --> src/main.rs:17:13
   |
10 |     let decoder_provider:DecoderProvider = Arc::new(
   |                          --------------- type annotation requires that `on_packet_render` is borrowed for `'static`
...
16 |         Box::new(|decoder| {
   |                  --------- value captured here
17 |             on_packet_render(None);
   |             ^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
21 | }
   | - `on_packet_render` dropped here while still borrowed

Box<dyn Foo> is shorthand for Box<dyn Foo + 'static> because of default trait object lifetimes.

If you want to box non-'static trait objects, you can use the syntax Box<dyn Foo + 'a>, like this:

pub type DecoderProvider = Arc<dyn for<'a> Fn(&Box<dyn Fn(&Option<&Arc<u8>>) + 'a>)>;

Playground

However, it's usually not useful or necessary to box an object with a non-'static type. Instead, you can replace the box with a reference. In particular, there's almost never a reason to pass &Box<T> instead of just &T.

use std::sync::Arc;
pub type DecoderProvider = Arc<dyn Fn(&dyn Fn(&Option<&Arc<u8>>))>;

fn main() {
    let on_packet_render =
        Arc::new(|packet: Option<Box<u8>>| {
            //render packet here
        });
    let decoder = Arc::new(0);
    let decoder_provider:DecoderProvider = Arc::new(
        move |b: &dyn Fn(&Option<&Arc<u8>>)| {
            b(&Some(&decoder));
        },
    );
    let provide_decoder: &dyn Fn(&Option<&Arc<u8>>) = 
        &|decoder| {
            on_packet_render(None);
        };
    
    decoder_provider(&provide_decoder);
}

Playground

3 Likes

in my case, T is almost always dyn Something, so shouldn't the Box be justified?

You can use &dyn Something (or &mut dyn Something) instead of Box or Arc in cases where the contents have a limited lifetime.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.