Lending Iterator 'static lifetime poisoning for IntoLendingIterator

I am currently trying to wrestle with the Lending Iterator problem as practice to improve my (poor) understanding of the borrow checker and I've run into quite a head-scratch-er that I can't figure out for the life of me.

Does anyone have any idea what I'm doing wrong, or is this one of those Polonius > NLL kind of things?

I see that lending-iterator also does not have a generically-implemented IntoLendingIterator, so this might be a repeat question, sorry if that's the case.

FYI: I do have a somewhat-working HKT trait to bind lifetimes without the hacky-workaround that lending-iterator and polonius-the-crab use, but it causes really bad lifetime poising in usage.

pub trait Lender {
    type Lend<'lend>: 'lend
    where
        Self: 'lend;
    fn next<'next>(&'next mut self) -> Option<<Self as Lender>::Lend<'next>>;
}
pub trait IntoLend {
    type Lend<'lend>: 'lend
    where
        Self: 'lend;
}
pub trait IntoLender: IntoLend
where
    for<'any> <Self as IntoLender>::Lender<'any>:
        Lender<Lend<'any> = <Self as IntoLend>::Lend<'any>>,
{
    type Lender<'lender>: 'lender
    where
        Self: 'lender;
    fn into_lender<'s>(self) -> <Self as IntoLender>::Lender<'s>
    where
        Self: 's;
}
impl<P: Lender> IntoLend for P {
    type Lend<'lend> = <Self as Lender>::Lend<'lend> where Self: 'lend;
}
impl<P: IntoLend> IntoLender for P
where
    for<'any> Self: 'any + Lender<Lend<'any> = <Self as IntoLend>::Lend<'any>>,
{
    type Lender<'lender> = Self where Self: 'lender;
    #[inline]
    fn into_lender<'s>(self) -> Self
    where
        Self: 's,
    {
        self
    }
}
fn main() {
    let mut x = 42u32;
    let _ = __test(&mut x);
}
fn __test<'x>(x: &'x mut u32) {
    /// MRE for Lender
    struct Bar<'baz, Baz: 'baz>(&'baz mut Baz);
    impl<'baz, Baz: 'baz> Lender for Bar<'baz, Baz> {
        type Lend<'lend> = &'lend mut Baz where Self: 'lend;
        fn next<'next>(&'next mut self) -> Option<<Self as Lender>::Lend<'next>> {
            Some(&mut self.0)
        }
    }
    // use MRE Lender
    {
        let mut bar: Bar<'x, u32> = Bar(x);
        let _ = bar.next();
        let _ = bar.next();
        // ????????????????????????????????????????????????????????
        let _ = <Bar<'x, u32> as IntoLender>::into_lender(bar);
        // ?????????????????????????????????  ^^^^^^^^^^^ ??????????
        let _ = 0;
    }
    ()
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
  --> src/lib.rs:56:17
   |
43 | fn __test<'x>(x: &'x mut u32) {
   |           -- lifetime `'x` defined here
...
56 |         let _ = <Bar<'x, u32> as IntoLender>::into_lender(bar);
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'x` must outlive `'static`

error: could not compile `playground` due to previous error

I guess the HRTB causes the trouble. Things like that means Self: 'static, Rust Playground .

I'm not sure. Maybe the bound should be Self: Lender<for<'any> Lend<'any> = <Self as IntoLend>::Lend<'any>> which is invalid syntax.

But this works (I modified the trait bounds on the associated types):

Rust Playground

1 Like

OMG thank you so much!

I guess I was completely overthinking it, I had assumed I would need a HRTB to allow Lender to implement IntoLender generically, because every other impl I've made using Lender needed a HRTB like:

impl<A: Lender, B: Lender> Lender for Chain<A, B>
where
    for<'any> B: 'any + Lender<Lend<'any> = <A as Lender>::Lend<'any>>

I guess my only question would be why the HRTB would induce a 'static like that?

If I understand correctly, a for<'any> means for any lifetime these constraints apply, not for<'all> for all lifetimes?

... or maybe I have just run into an ill-formed compiler error as a result of misusing HRTBs? I think it's more likely that I'm misunderstanding HRTBs

If the constraints apply for any lifetime then in particular they apply for 'static.

1 Like

Writing lending iterators is hard (also even with GATs). We can write a shorter version that applies for your testing:

pub trait Lender {
    type Lend<'lend> where Self: 'lend;
    fn next<'next>(&'next mut self) -> Option<Self::Lend<'next>>;
}

pub trait IntoLender {
    type Lender<'lender>: Lender where Self: 'lender;
    fn into_lender<'s>(self) -> Self::Lender<'s> where Self: 's;
}

impl<P: Lender> IntoLender for P {
    type Lender<'lender> = Self where Self: 'lender;

    #[inline]
    fn into_lender<'s>(self) -> Self where Self: 's,
    {
        self
    }
}

And add a generic test:

fn test_generics<L: IntoLender>(l: L)
where
    for<'lender, 'lend> L::Lender<'lender>: Lender<Lend<'lend> = &'lend mut String>,
{
    let mut lender = l.into_lender();
    ...

Everything compiles... except for real usage Rust Playground


Suggestions to write lending iterators:

2 Likes

Thank you for the reading recommendations, they were exactly what I needed!

I think I probably got more than what I was bargaining for when I decided to see what the Lending Iterator problem could teach me about the borrow checker, I definitely need a break for now, but I learned a heck ton!

You learn the best by failing to do challenging things, as they say.

With my new found knowledge about the bounds of the borrow checker, I'm now better equipped to shill for Rust to all my friends and family! The U.S. gov security recommendation should've covered that for me, but you can never be too prepared :rofl:.