Home > Software engineering >  Issue returning an abstract type with a higher rank trait bound
Issue returning an abstract type with a higher rank trait bound

Time:01-06

I'm trying to write a generic trait for sending messages between processes. The simplified version of it looks like this:

trait Sender<T> {
    fn send_msg(&mut self, to: u64, body: T) -> Result<()>;
}

I want to be able to send messages that contain references, so as to avoid copying data:

#[derive(Serialize)]
enum MsgBody<'a> {
    Success,
    Fail(Error),
    Write { offset: u64, buf: &'a [u8] },
}

However I've run into an issue where the type parameter T can only refer to a single lifetime, preventing references of any lifetime from being used. To illustrate this consider this dummy implementation of Sender<T>:

struct SenderImpl<T>(PhantomData<T>);

impl<T: Serialize> Sender<T> for SenderImpl<T> {
    fn send_msg(&mut self, to: u64, body: T) -> Result<()> {
        Ok(())
    }
}

Now if I try to return a sender that will work for any lifetime parameter

fn ref_sender() -> impl for<'a> Sender<MsgBody<'a>> {
    SenderImpl(PhantomData)
}

then I get a compilation error because the type parameter T is not generic over all possible lifetimes:

error: implementation of `Sender` is not general enough
  --> src/lib.rs:29:5
   |
29 |     SenderImpl(PhantomData)
   |     ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Sender` is not general enough
   |
   = note: `SenderImpl<MsgBody<'2>>` must implement `Sender<MsgBody<'1>>`, for any lifetime `'1`...
   = note: ...but it actually implements `Sender<MsgBody<'2>>`, for some specific lifetime `'2`

The error makes sense but I'm not sure how to express what I want. What I'd like to say is this

fn ref_sender() -> impl for<'a> Sender<MsgBody<'a>> {
    SenderImpl(PhantomData::<for<'a> MsgBody<'a>>)
}

but of course for<'a> MsgBody<'a> is not actually a type.

I can work-around this by using MsgBody in the Sender trait rather than making it generic (you can see that here). I don't like this solution though as it sacrifices flexibility.

Is there a way to get the generic Sender<T> to work with types that have a lifetime parameter?

Edit: Here's the code used in this question: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0aac9134639e9593698bfe9da7722d9f

CodePudding user response:

Instead of making the trait generic, move the generic type parameter to send_msg:

trait Sender {
    fn send_msg<T: Serialize>(&mut self, to: u64, body: T) -> Result<()>;
}

Then every call to send_msg can use a different lifetime parameter. The full solution is here.

CodePudding user response:

As you've noted, &T needs a lifetime to be a complete type and for<'a> &'a T doesn't exist. The concrete problem is that SenderImpl<T> must have a complete type T and only implements Sender with that type.

You can workaround this by avoiding references in SenderImpl<T> (by making T = [u8]) and allow it to implement Sender<&T> for any lifetime:

struct SenderImpl<T: ?Sized>(PhantomData<T>);

impl<'a, T: Serialize   ?Sized> Sender<&'a T> for SenderImpl<T> {
    fn send_msg(&mut self, to: u64, body: &'a T) -> Result<()> {
        Ok(())
    }
}

Edit:

This approach doesn't seem to work in the case where T has it's own lifetime parameter.

You could also make it generic over any specific type with lifetimes, just SenderImpl won't have a specific type associated with it beyond what the trait implementation implies:

struct SenderImpl;

impl<'a> Sender<MsgBody<'a>> for SenderImpl {
    fn send_msg(&mut self, to: u64, body: MsgBody<'a>) -> Result<()> {
        Ok(())
    }
}

fn ref_sender() -> impl for<'a> Sender<MsgBody<'a>> {
    SenderImpl
}

Or, similarly to the self-answer, you can implement Sender<T> for any type. But like the above, SenderImpl obviously won't be specific to any in particular:

struct SenderImpl;

impl<T: Serialize> Sender<T> for SenderImpl {
    fn send_msg(&mut self, to: u64, body: T) -> Result<()> {
        Ok(())
    }
}

fn ref_sender() -> impl for<'a> Sender<MsgBody<'a>> {
    SenderImpl
}
  • Related