Can someone help me constrain this lifetime parameter

trait Dummy {
    type Var;
    
    fn foo(&mut self) -> Self::Var;
}


pub enum Acter<'a, T: 'a> {
    Doing(&'a mut [T]),
    Staying(&'a mut [T]),
}

pub struct Dupe<D: Dummy> {
    dummy: D,
}

// Error here: the lifetime parameter `'a` is not constrained
impl<'a, D> Dummy for Dupe<D>
where
    D: Dummy,
    D::Var: 'a,
{
    type Var = Acter<'a, D::Var>;

    fn foo(&mut self) -> Self::Var {
        unimplemented!()
    }
}

Also tried

// Error here: the lifetime parameter `'a` is not constrained
impl<'a, T, D> Dummy for Dupe<D>
where
    D: Dummy<Var = T>,
    T: 'a,
{
    type Var = Acter<'a, T>;

    fn foo(&mut self) -> Self::Var {
        unimplemented!()
    }
}

I also tried to compromise with

impl<'a, T, D> Dummy for Dupe<D>
where
    D: Dummy<Var = &'a mut T>,
    T: 'a + Sized
{
    type Var = Acter<'a, T>;

    fn foo(&mut self) -> Self::Var {
        unimplemented!()
    }
}

But that leaves me with an even scarier the size for values of type[i16]cannot be known at compilation time when I try to use it

impls_Dummy.foo();

Two options:

  • Add a lifetime to the trait.
trait Dummy<'a>: 'a {
    type Var;
    
    fn foo(&'a mut self) -> Self::Var;
}
// One day...
trait Dummy {
    type Var<'a>;
    
    fn foo(&mut self) -> Self::Var<'_>;
}

If you are not in control of the trait… that’s tough. All I can say is that the current signature of Dummy makes it impossible to do what you want to do.

Crap. Dummy is actually std::Iterator… Guess I should just make Actor take ownership of T and not take a reference. Which means I’ll have to copy over my data structures which kinda sucks. Could I make a super trait by extending Dummy and add the lifetime “somehow”? Then I could just have Acter impl the super trait.

Uhh well I got it to compile somehow which means I didn’t explain everything correctly. Here’s the full code of what I’m doing https://play.rust-lang.org/?gist=3b0e37c15d2220f357df2fedcae80427&version=stable&mode=debug&edition=2015

Does that look right?

Nah, I think that was my mistake for jumping to conclusions after seeing so many similar threads!

Yes, you’re right, you can write an iterator that produces things bound to the lifetime of Self, as you have. All of the items produced will have the same lifetime, but for use cases such as chunks_mut, this is not an issue.

The thing that cannot be done without modifying the trait is to let each item have a different lifetime. (i.e. bound to the &mut self argument of Iterator::next). This would enable methods like windows_mut, where the returned items alias each other (and therefore each one must fall out of scope before you can call next again).

Ah. I think though I did make it confusing, in my eagerness to dummy down the example and not specifying Iterator.

I’ll admit I don’t fully understand lifetimes enough to comprehend this. I just sorta play Type Tetris until the compiler gives me the okay.

I don’t know what I’ll do when I need to know more advanced interactions like with windows_mut but hopefully I’ll know more about lifetimes by then.

The Iterator trait is designed to allow collect(), so user of the iterator can always be able to use all of the items, at the same time, even after the iteration has finished and the Iterator has been destroyed.

Because of that it’s not possible to lend any temporary value from the iterator object. It can only pass things through.

Note also that lifetimes don’t do anything. They don’t make the compiler emit any code. They only explain to the compiler what the code does anyway, and let the compiler check this information is consistent.

2 Likes