Help defining lifetime constraints in a struct definition

I'm trying to define a trait that, in certain implementations (but not all), will have functions that take in references to buffer structs. I want to constrain the lifetimes of the references to the structs such that they can't outlive the lifetime of the buffer inside the struct. I don't have any issues doing that directly in an implementation, but when I add one more layer (Wrap in my example code) I can't figure out how to properly constrain my lifetimes.

In my example code, I understand that for<'a,'ba> is telling the compiler that it's supposed to be valid for any 'a and any 'ba, but I don't want to tell it that. I can't figure out any way to constrain the lifetimes introduced by for<> (which is fair if it means what I think it does) nor can I figure out how else I could introduce the lifetimes in the Wrap struct definition.

pub trait Foo<'a, A> {
  fn bar(&self, input: &'a A) -> ();
}

pub struct Buffer<'a> {
  a: &'a [u8],
}

pub struct FooImpl {}
impl<'a, 'ba:'a> Foo<'a, Buffer<'ba>> for FooImpl {
  fn bar(&self, _: &'a Buffer<'ba>) -> () {}
}

pub struct Wrap<A>
where
  A: for<'a,'ba> Foo<'a, Buffer<'ba>>
{
  a: A,
}

pub fn main() -> () {
  Wrap { a: FooImpl {} };
}
   Compiling playground v0.0.1 (/playground)
error: implementation of `Foo` is not general enough
  --> src/main.rs:22:3
   |
22 |   Wrap { a: FooImpl {} };
   |   ^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
   |
   = note: `FooImpl` must implement `Foo<'0, Buffer<'1>>`, for any two lifetimes `'0` and `'1`...
   = note: ...but it actually implements `Foo<'_, Buffer<'2>>`, for some specific lifetime `'2`

error: could not compile `playground` (bin "playground") due to 1 previous error

playground link: Rust Playground

I'm still learning Rust, so if someone could help me understand what specific documentation is relevant to this issue, that would be greatly appreciated!

You're referring to this I guess?

impl<'a, 'ba: 'a> Foo<'a, Buffer<'ba>> for FooImpl {
//       ^^^^^^^

Sometimes bounds like that are implied. When they're implied, you don't have to state them explicitly. Not stating such bounds explicitly can make implementations more general.

For example, in this method:

    fn bar(&self, input: &'a A) -> ();

A: 'a is implied by the presence of &'a A in the inputs. If this is the only reason you need the bound, you can just leave the the bound off your implementation:

impl<'a, 'ba> Foo<'a, Buffer<'ba>> for FooImpl {
    fn bar(&self, _: &'a Buffer<'ba>) -> () {}
}

pub struct Wrap<A>
where
    A: for<'a, 'ba> Foo<'a, Buffer<'ba>>,
{
    a: A,
}

And that compiles.


If there are other reasons you need the bound, you can sometimes "smuggle" an implicit bound into the trait itself by way of having a default type parameter that implies the bound you need:

//                   vvvvvvvvvvvvvv
pub trait Foo<'a, A, _Bound = &'a A> {
    fn bar(&self, input: &'a A) -> ();
}

// Due to the default parameter, the below is really:
// ```
// impl Foo<'a, Buffer<'ba>, &'a Buffer<'ba>> for FooImpl
// ```
impl<'a, 'ba> Foo<'a, Buffer<'ba>> for FooImpl {
    fn bar(&self, _: &'a Buffer<'ba>) -> () {}
}

Your OP doesn't need this level of workaround, though.


It's generally better to only state bounds on structs where they are needed. You don't need them on the declaration of Wrap<A>, so I'd just leave them off:

pub struct Wrap<A> {
    a: A,
}

Otherwise you'll have to restate the bounds pretty much everywhere, even if you don't use them.

Presumably you do need the bound somewhere else, so this is just a side-note (not a solution to your question).


Implied bounds are probably in the official documentation somewhere, but I'm unaware of any official documentation that walks through utilizing them like this or how higher-ranked bounds work at this level of detail (unless you include things like the compiler dev guide).

Thanks for the help and info! Plenty of new things for me to learn about in those examples you provided.

Presumably you do need the bound somewhere else, so this is just a side-note (not a solution to your question).

Apparently I do not. I was able to delete the lifetimes from my real code without issue (although I did not think I could until I read your response). Embarrassingly, I made a mistake in duplicating the lifetimes I was using when I refactored from slices to structs wrapping those slices.

The function definition for my proof of concept looked similar to this (initially it had no lifetime, but at some point I added code somewhere and the compiler complained about lifetimes and I added one):

fn bar(&self, buffer: &'a [u8]) -> ();

And then I created a struct to wrap the slices and passed that by reference and in doing so duplicated the lifetime:

fn bar(&self, buffer: &'a Buffer<'a>) -> ();

At some point while I was reading my code I realized that it was silly to constrain all references to the buffers as having the same lifetimes as the underlying slices, and so I added the ba lifetime to the definition and the compiler complained about things not living long enough so I added the 'ba: 'a constraint. That was fine until I eventually created the Wrap struct and then my past mistakes caught up with me.

So now my code is a lot cleaner and looks something like this:

pub struct FooImpl {}
impl<'a> Foo<Buffer<'a>> for FooImpl {
  fn bar(&self, _: &Buffer<'a>) -> () {}
}

pub struct Wrap<A>
where
  A: for<'a> Foo<Buffer<'a>>
{
  a: A,
}

Almost all of the answers for people's questions about Rust's lifetimes on the internet are: "You can't do that" or "Elide the lifetimes" and I was really hoping my question wouldn't fall into one of those groups :frowning:

Thanks again for the explanation (especially the "implied bounds" term as that at least gets me to an RFC about it) and sorry for wasting your time giving me solutions to problems I didn't have (but thought I did).

Glad it's useful and I don't feel my time was wasted. :slight_smile:

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.