A trait that produces something with 'static or 'a lifetimes

I'm doing an exercise about lifetimes and I thought of a trait Decoder. Here's a struct for simplicity, but let's say I want to design a trait for video decoders. Video decoders can either

  • copy the decoded data into a 'static packet
  • provide a reference to the data
  • provide a pointer to the data in GPU, that can be passed to OpenGL, no frame is copied to RAM ever

I think the list above covers all possible cases for video decoders, which is important for a trait.

Here's my attempt

use std::sync::Arc;

/// Here would go: `fn get_data(&self) -> &[u8]`, `fn get_width(&self) -> usize`, etc
pub trait DecodedPacket<'a> {}

pub struct ConcretePacket {}

pub struct SlicePacket<'a>{
    pub slice: &'a [u8]
}

impl<'a> DecodedPacket<'a> for ConcretePacket {}
impl<'a> DecodedPacket<'a> for SlicePacket<'a> {}

pub struct Decoder<'c> {
    pub slice: &'c [u8]
}

impl<'c> Decoder<'c> {
    /// This can return packets that either contain references
    pub fn receive_ref(
        &self,
        on_packet: Arc<dyn for<'a, 'b> Fn(Box<dyn DecodedPacket<'a>>)>,
    )  {
        // case 1: slice lifetime is made inside this function
        let slice:&[u8] = &[1,2,3];
        let packet: Box<dyn DecodedPacket<'_>> = Box::new(SlicePacket{slice: slice});
        on_packet(packet);
        
        // case 2: slice lifetime is from outside the function
        let packet: Box<dyn DecodedPacket<'_>> = Box::new(SlicePacket{slice: slice});
        on_packet(packet);
    }
    /// This can return packets that are created without any references
    /// Advantages: I can move to another thread, and I can downcast
    /// Why downcasting is needed? Well, there are packets that hold no 
    /// data but a pointer to data on GPU, but also packets that might need very
    /// specific treatment because they are very different
    pub fn receive(
        &self,
        on_packet: Arc<dyn Fn(Box<dyn DecodedPacket<'static>>)>,
    )  {
        let packet: Box<dyn DecodedPacket<'_>> = Box::new(ConcretePacket{});
        on_packet(packet);
    }
}

fn main() {
    let slice = &[1,2,3];
    let decoder = Decoder{
        slice: slice
    };
    let on_packet = Arc::new(|_packet: Box<dyn DecodedPacket<'static>>|{
        println!("packet received"); 
    });
    decoder.receive(on_packet);
    
    let on_packet = Arc::new(|_packet: Box<dyn DecodedPacket>|{
        println!("packet received"); 
    });
    decoder.receive_ref(on_packet);
}

Playground

I tried to conciliate both the 'static case and reference case in the same function but I couldn't. I couldn't make the caller determine if the data should be static or reference. Do you guys think I did exaggerate? Or is this a good trait?

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.