Lifetime: cannot infer an appropriate lifetime for autoref due to conflicting requirements

#1

Hi:
I’m struggling with lifetime right now. And I have no idea about why this error is occurred and what does it mean.
Sorry for that, I just upload the whole code.
Here is the code.
And here is the error:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> lib_xch/src/public/handler.rs:80:56
   |
80 |                     .insert("Parser", fromcell(self.ds.get("Parser").unwrap())?);
   |                                                        ^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 74:5...
  --> lib_xch/src/public/handler.rs:74:5
   |
74 | /     pub fn parse(&mut self) -> Result<DataSetOut<&T>, ErrorCases> {
75 | |         match parser::<Cell<T>>(self.equ)? {
76 | |             (cd, data) => {
77 | |                 self.cd = cd;
...  |
83 | |         Ok((&self.cd, &self.ds_raw.get("Parser").unwrap()))
84 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> lib_xch/src/public/handler.rs:80:48
   |
80 |                     .insert("Parser", fromcell(self.ds.get("Parser").unwrap())?);
   |                                                ^^^^^^^
note: but, the lifetime must be valid for the lifetime 'b as defined on the impl at 42:9...
  --> lib_xch/src/public/handler.rs:42:9
   |
42 | impl<'a,'b, T: CheckedType + CheckedCalc> Handler<'a,'b, T>
   |         ^^
   = note: ...so that the expression is assignable:
           expected std::vec::Vec<std::vec::Vec<&'b T>>
              found std::vec::Vec<std::vec::Vec<&T>>

Thank you!

0 Likes

#2

There isn’t enough there for me to really diagnose the issue (lots of missing function signatures, type aliases…), but it looks like the value you’re trying to return from parse has type Result<DataSetOut<&'b T>>, when the signature is (&'c self) -> Result<DataSetOut<&'c T>>.

It is unclear to me why Handler has two lifetime parameters; all of the places where lifetimes appear in it are covariant, so it should only need one.

0 Likes

#3

It looks like you’re trying to create a self referential struct - you can’t borrow from ds and then store that reference in ds_raw - that will result in the self reference, which isn’t supported in Rust.

0 Likes

#4

But if I write in this way:

pub fn parse(&mut self) -> Result<DataSetOut<&T>, ErrorCases> {
        match parser::<Cell<T>>(self.equ)? {
            (cd, data) => {
                self.cd = cd;
                self.ds.insert("Parser", data.clone());
                self.ds_raw
                    .insert("Parser", fromcell(&data)?);
            }
        }
        Ok((&self.cd, &self.ds_raw.get("Parser").unwrap()))
    }

Then rust will complain:

error[E0597]: `data` does not live long enough
  --> lib_xch/src/public/handler.rs:80:48
   |
42 |   impl<'a,'b, T: CheckedType + CheckedCalc> Handler<'a,'b, T>
   |           -- lifetime `'b` defined here
...
79 | /                 self.ds_raw
80 | |                     .insert("Parser", fromcell(&data)?);
   | |________________________________________________^^^^^__- argument requires that `data` is borrowed for `'b`
   |                                                  |
   |                                                  borrowed value does not live long enough
81 |               }
82 |           }
   |           - `data` dropped here while still borrowed

I don’t know how to design that?

0 Likes

#5

Actually ds_raw stores fromcell(&....) which is Vec<Vec<&'b T>>.
Why it will be a self reference?

0 Likes

#6

Because equ may live much longer than &T?

0 Likes

#7

ds is the following field:

ds: HashMap<&'static str, Vec<Vec<Cell<T>>>>

Any ds.get(...) call returns you a reference to the owned Vec in there. Note that Handler actually owns those vectors.

fromcell just returns you a reference tied to its input arg, which happens to be a reference into ds:

fn fromcell<T: Clone>(v: &[Vec<Cell<T>>]) -> Result<Vec<Vec<&T>>, ErrorCases>

ds_raw does want a &'b lifetimed reference, but you don’t have one here nor can you get one like that from ds (as things stand).

But what your code is trying to do, taking the exact compiler errors out of the picture for a minute, is create a self reference given the above - you’re trying to insert a reference into some piece of ds (note again that ds owns its values) into ds_raw.

0 Likes

#8

But fromcell should return a Vec. why it will return a reference? You mean the &T inside the Vec ?
If I want ds_raw own the value. what should I do?

And is my design of lifetime in here right?

pub struct Handler<'a,'b, T> {
    equ: &'a str,
    ds: HashMap<&'static str, Vec<Vec<Cell<T>>>>,
    ds_raw: HashMap<&'static str, Vec<Vec<&'b T>>>,
    cd: ChemicalEquation,
}

Sorry for a lot of question.

0 Likes

#9

Yes, it’s the &T there.

You mean you want it to own the Ts, rather than holding a vec of &'b Ts?

I don’t really know what this code is doing in the grand scheme of things, so hard to suggest anything. But, a few options:

  1. If T is Clone, ds and ds_raw can store their own copies.
  2. ds and ds_raw can hold Rc<T> (or Arc<T>, if you need threadsafe refcounting)
  3. Store Ts entirely elsewhere, and instead have ds and ds_raw hold a lightweight (e.g.) usize that identifies their location in that other storage (this is basically an arena setup).
  4. Similar to the previous one, make ds and ds_raw hold references to the Ts, and store those Ts elsewhere.
  5. If either ds or ds_raw holds a given T, then remove that T from ds and insert into ds_raw.

It’s not right based on what your code is trying to do :slight_smile:. Whether its that design or the code itself that’s wrong is hard to say since, as mentioned, I don’t really know what you’re doing overall.

0 Likes

#10

I want to store the Vec<Vec<Cell<T>>> in ds and use fromcell to store Vec<Vec<&T>> into ds_raw. the &T and ds has the same lifetime.

So that is a self reference because all the value in there are owned by Handler, and ds in Handler want to make a reference of Handler where it is owned? In general, a value inside Handler refer to Handler?

If what I understood is right, self.a = &self.b is also not allowed in Rust?

0 Likes

#11

That’s right - you cannot have a struct hold (directly or indirectly) a reference to itself, at least not in any reasonable manner. There’re crates, like owning_ref and rental, that support a limited form of this - IMO, they’re a last resort. Try a different design first.

Sort of. You can do something like the following:

struct SelfRef<'a> {
    x: i32,
    y: Option<&'a i32>,
}

fn main() {
    let mut sr = SelfRef { x: 1, y: None };
    {
        sr.y = Some(&sr.x);
        sr.y = None;
    }
    let sr2 = sr; // does not compile
}

But as the comment there says, once you take that borrow to a part of self, you’ve frozen sr in place - cannot move it as it’s perma-borrowed. So it’s a “semi-decent” way to pin the struct, but it’s not a workable setup for general purpose.

0 Likes

#12

I re-designed it like this.

pub struct Handler<'a, T> {
    equ: &'static str,
    ds: &'a mut HashMap<&'static str, Vec<Vec<Cell<T>>>>,
    ds_raw: HashMap<&'static str, Vec<Vec<&'a T>>>,
    cd: ChemicalEquation,
}
pub fn new(equ: &'static str,ds: &'a mut HashMap<&'static str, Vec<Vec<Cell<T>>>>) -> Self {
        Handler {
            equ,
            ds,
            ds_raw: HashMap::new(),
            cd: ChemicalEquation::new(),
        }
    }
    pub fn parse(&mut self) -> Result<DataSetOut<&T>, ErrorCases> {
        match parser::<Cell<T>>(self.equ)? {
            (cd, data) => {
                self.cd = cd;
                self.ds.insert("Parser", data.clone());
                self.ds_raw
                    .insert("Parser", fromcell(self.ds.get("Parser").unwrap())?);
            }
        }
        Ok((&self.cd, &self.ds_raw.get("Parser").unwrap()))
    }

But why rust still complain about that? That’s weird…
This time there should be no self reference?

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> lib_xch/src/public/handler.rs:80:57
   |
80 |                     .insert("Parser", fromcell(&self.ds.get("Parser").unwrap())?);
   |                                                         ^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 74:5...
  --> lib_xch/src/public/handler.rs:74:5
   |
74 | /     pub fn parse(&mut self) -> Result<DataSetOut<&T>, ErrorCases> {
75 | |         match parser::<Cell<T>>(self.equ)? {
76 | |             (cd, data) => {
77 | |                 self.cd = cd;
...  |
83 | |         Ok((&self.cd, &self.ds_raw.get("Parser").unwrap()))
84 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> lib_xch/src/public/handler.rs:80:49
   |
80 |                     .insert("Parser", fromcell(&self.ds.get("Parser").unwrap())?);
   |                                                 ^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 42:6...
  --> lib_xch/src/public/handler.rs:42:6
   |
42 | impl<'a, T: CheckedType + CheckedCalc> Handler<'a, T>
   |      ^^
   = note: ...so that the expression is assignable:
           expected std::vec::Vec<std::vec::Vec<&'a T>>
              found std::vec::Vec<std::vec::Vec<&T>>

Any explanation of the error? Thank you very much!

0 Likes

#13

The problem now is because you cannot borrow &mut self for an elided lifetime and return a &'a T reference. Handler has an exclusive (mutable) borrow of ds for 'a but this is not much different from owning the HashMap - you cannot increase the scope of the borrow like that. If ds were an immutable borrow then what you have would work. But holding a mutable borrow, in a lot of ways, is same as owning the value - you cannot return a reference with a lifetime that outlives borrow of self if the data is owned.

Here, you’d need to change parse to take &'a mut self but this will borrow Handler for its entire lifetime so will be useless.

My suggestion would be to try using one of the other options I listed, perhaps something without lifetimes at all, for now.

0 Likes

#14

So I could only use the 2 as you mentioned before?
I don’t want to clone T because I did that before. I thought it is expansive. BTW, is it expansive?

0 Likes