Compiler forces Arc to be 'static but I need it to be 'a

I'm trying to create a trait for a Decoder that can return a packet that contains a reference inside. That is, I don't need to copy the packet to an owned object just so it's 'static. Here's the sketch:

use std::sync::Arc;

pub trait DecodedPacket<'a>: Send {}

pub type OnConsume = Arc<dyn Fn() -> Option<Box<dyn for<'a> DecodedPacket<'a>>> + Send + Sync>;

pub trait Decoder: Send{
    fn receive_ref(&self,on_packet: Arc<dyn for<'a> FnMut(Box<dyn DecodedPacket<'a>>)>);
}

pub struct DummyDecoder {}

impl Decoder for DummyDecoder {
    fn receive_ref(&self,_: Arc<dyn for<'a> FnMut(Box<dyn DecodedPacket<'a>>)>){
        unimplemented!();
    }
}

struct DummyRenderer {
    on_consume: OnConsume
}

impl DummyRenderer {
    pub fn render(&self) {
        let packet = (self.on_consume)();
        //Render packet somehow here
    }
}

fn main() {
    let decoder: Box<dyn Decoder + Sync> = Box::new(DummyDecoder{});
    let renderer_on_consume: OnConsume =
        Arc::new(move || -> Option<Box<dyn DecodedPacket<'_>>> {
            let decoded_packet;
            let on_packet = Arc::new(|packet: Box<dyn DecodedPacket<'_>>|{
                decoded_packet = Some(packet);
            });
            decoder.receive_ref(on_packet);
            decoded_packet
        });
    let dummy_renderer = DummyRenderer{
        on_consume: renderer_on_consume
    };
    dummy_renderer.render();
}

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

You can see that receive_ref expects a function that can deal with all lifetimes, so it should work for a small non 'static lifetime. However, the compiler is making all the lifetimes 'static:

Error:

error[E0308]: mismatched types
  --> src/main.rs:33:29
   |
33 |         Arc::new(move || -> Option<Box<dyn DecodedPacket<'_>>> {
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected enum `Option<Box<(dyn for<'a> DecodedPacket<'a> + 'static)>>`
              found enum `Option<Box<dyn DecodedPacket<'_>>>`

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:38:33
   |
38 |             decoder.receive_ref(on_packet);
   |                                 ^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 35:38...
  --> src/main.rs:35:38
   |
35 |               let on_packet = Arc::new(|packet: Box<dyn DecodedPacket<'_>>|{
   |  ______________________________________^
36 | |                 decoded_packet = Some(packet);
37 | |             });
   | |_____________^
note: ...so that the type `[closure@src/main.rs:35:38: 37:14]` will meet its required lifetime bounds
  --> src/main.rs:38:33
   |
38 |             decoder.receive_ref(on_packet);
   |                                 ^^^^^^^^^
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
  --> src/main.rs:38:33
   |
38 |             decoder.receive_ref(on_packet);
   |                                 ^^^^^^^^^
   = note: expected `Arc<(dyn for<'a> FnMut(Box<(dyn DecodedPacket<'a> + 'static)>) + 'static)>`
              found `Arc<dyn for<'a> FnMut(Box<(dyn DecodedPacket<'a> + 'static)>)>`

error[E0308]: mismatched types
  --> src/main.rs:39:13
   |
39 |             decoded_packet
   |             ^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected enum `Option<Box<(dyn for<'a> DecodedPacket<'a> + 'static)>>`
              found enum `Option<Box<dyn DecodedPacket<'_>>>`

To be fair I'm kinda confused about when to put '_ and how for<'a> works inside an Arc. What does it mean for an Arc to be able to expect all lifetimes?

Here's how far I got. I don't know if it's a useful path or not, but perhaps that error indicates a blocker to your design.

What your implementations look like will determine how general your dyn objects can be. And you can go from for<'a> ... to a specific lifetime but not vice-versa. Examples.

I was trying to evict putting a lifetime parameter on OnConsume because I'm not sure if polluting the Renderer (which holds OnConsume) with a lifetime will have consequences in the future. I think your solution might be ok so let's focus on the problem that is left.

Since Renderer has its own thread, I cannot call it directly when a packet is decoded. So I must force Renderer's thread to get the packets from the decoder. That's why it has an OnConsume function. So the idea is that this OnConsume function should be able to provide the renderer with a packet that should contain references inside so no second copy is needed.

let renderer_on_consume: OnConsume =
    Arc::new(move || -> Option<Box<DecodedEverywhere<'a>>> {
        let mut decoded_packet = None;
        let on_packet = Arc::new(|packet: Box<DecodedEverywhere<'b>>|{
            decoded_packet = Some(packet);
        });
        decoder.receive_ref(on_packet);
        decoded_packet
    });

it looks like the problem is that the lifetime 'b of the packet received from decoder through on_packet could be shorter that the lifetime 'a of the packet returned from renderer_on_consume.

It's tempting to add a lifetime parameter to the decoder also and somehow force this parameter to be larger than the one from the renderer but I can't. The decoder could produce packets directly from the GPU which have no lifetime (they come from C).

The thing that cannot change, in my opinion, is the OnConsume function. The renderer absolutely needs a way to get packets through a callback.

Could also the problem be related to fn receive_ref<'s>? This lifetime parameters forces the lifetime to be something, while a function that deals with all lifetimes should be better.

here's my new attempt: Rust Playground but I still have lifetime problems on transfering from one closure to the other.

This is an interesting problem for me, I was wondering what could be done

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.