Compiler seems confused about the argument lifetime

Why the compiler wants the function parameter to outlive the call block in this example? It only happens if the method takes &mut self though.

struct Demo<T>(Option<T>);

struct Obj {
    a: Vec<u8>,
    b: Vec<u8>,
}

impl<T> Demo<T> {
    fn non_mut_self(&self, _: &T) {}
    fn mut_self(&mut self, _: &T) {}
    fn something(&mut self) {}
}

fn happy(s: impl Iterator<Item = Result<Obj, ()>>) {
    let mut demo = Demo(None);
    for obj in s {
        let obj = obj.unwrap();
        let pair = (&obj.a, &obj.b);
        demo.non_mut_self(&pair); // << happy borrow
    }
    demo.something();
}

fn sad(s: impl Iterator<Item = Result<Obj, ()>>) {
    let mut demo = Demo(None);
    for obj in s {
        let obj = obj.unwrap();
        let pair = (&obj.a, &obj.b);
        demo.mut_self(&pair); // << sad borrow
    }
    demo.something();
}

fn main() {
    happy(vec![Ok(Obj { a: vec![], b: vec![] })].into_iter());
    sad(vec![Ok(Obj { a: vec![], b: vec![] })].into_iter());
}

Snippet https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e9211d8c0ae202f9a7f2cb2d2bc4cf0a

Edit: use a realistic example

It looks to me it should work.

Usually the problem is that &mut makes lifetimes invariant, and that complicates lots of things. However, here the lifetime of self and the argument are separate, so it shouldn’t matter. Maybe it’s a bug?

let i = 1;
let mut d: Demo<&'static u32>  = Demo(Some(&1));
d.non_mut_self(&&i);
d.mut_self(&&i);

Non mut works because the compiler allows variant.

Old example can force to fixed life;

fn sad<'f, 't:'f, T: std::fmt::Debug + 't>(s: &'f [T]) {
    let mut demo: Demo<(&'f T,&'f T)> = Demo(None);
    for a in s {
        let b = &a;
        demo.mut_self(&(b, b));
    }
    demo.something();
}

New code you have changed to is completely different case and more like first snippet I show. A fix is to use another type. Ideally “higher kinded types” would work but not something rust currently has.

1 Like

The lifetime playing a role here is the one within <T> itself. Long story short:

pair: T unless “covariance can help it”

Since demo exists outside the for loop, (all the elements of) type T need(s) to outlive that loop too:

  • T : 'loop

But we have an element pair: (&'a Vec<u8>, &'a vec<u8>),
where we don’t have that 'a : 'loop.

  • I will write this as 'a !: 'loop

So, when type checking, the compiler must match

  • &_ Demo<T> : &_ Demo<U>

    with

  • (&'a Vec<u8>, &'a vec<u8>) : U, where 'a !: 'loop

The last constraint implies that U !: 'loop.

So now we have:

  • type F<X> = &mut X is invariant:

    • thus in the &mut Self case U must be T and thus T !: 'loop, hence the error;
  • type F<X> = &X is covariant:

    • thus in the &Self case, the compiler can choose a different U (as long as it verifies T : U), thus no longer condemning T to not outliving 'loop.

QED

1 Like

I’d understand if demo and s were related, but they aren’t. How can two objects that are guaranteed not to overlap, can influence each other?

fn mut_self<'a, 'b>(&'a mut self, _: &'b T) {}

Why is variance of 'a limiting variance of 'b?

This is not about 'a and 'b but Self and T

(by the way, for all T, type F<'x> = &'x mut T is actually covariant)

The same would arise with the following function:

fn set<T> (at_opt: &'_ mut Option<T>, value: T)
{
    // *at_opt = Some(value) /* for instance */
}

fn main ()
{
    let mut x: Option<&i32> = None;
    {
         let y = 42;
         set(&mut x, &y);
    }
    dbg!(x);
}
2 Likes

For the old example, shouldn’t the compiler figure it out? I assumed that’s what should happen by default already (you can’t have a slice that doesn’t outlive its content, and references to its elements should have the same lifetime as the slice).

Ouch, this is much more convoluted than I though. I can’t parse a solution from the answers so far.

Is there any way I can get the current snippet to compile?

The problem comes from pair being local to the loop i.e., you are borrowing after iterating. There is no way your code can compile given that. But since you seem to only require references, there is an easy solution: borrow before iterating.

with Vecs (for readability)

/// borrows at call site
fn sad<'elems> (s: &'elems Vec<Result<Obj, ()>>) // could even be &[Result<Obj, ()>]
{
    let mut demo = Demo(None);
    'loop : for obj in s {
        let obj = obj.as_ref().unwrap();
        let pair = (&obj.a, &obj.b); // reborrowing enables this outlive 'loop
        demo.mut_self(&pair); // all is fine
    }
    demo.something();
}

generic solution

/// Prefer `IntoIterator` bound to `Iterator` (more ergonomic to call)
fn sad<'elems> (s: impl IntoIterator<Item = &'elems Result<Obj, ()>>) {
    let mut demo = Demo(None);
    for obj in s {
        let obj = obj.as_ref().unwrap();
        let pair = (&obj.a, &obj.b);
        demo.mut_self(&pair); // all is fine
    }
    demo.something();
}

fn main() {
    happy(vec![Ok(Obj { a: vec![], b: vec![] })].into_iter());
    sad(&vec![Ok(Obj { a: vec![], b: vec![] })]);
}

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.