Unable to generalized extracting data using TryFrom

I am trying to extract data using TryFrom, where there is a header, data, and footer. However, I am fighting with lifetimes.

My gist of this code. Confusing Borrow Behavior · GitHub

It is a simplified version of what I am trying to do.

The basic idea is a generalized struct that wraps the data extraction.

struct MyData<T>(String, T, String);

impl <'a, 'b, T> TryFrom<&'a mut DataReader<'b>> for MyData<T>
where
T: TryFrom<&'a mut DataReader<'b>>,
T::Error: From<ErrorKind>,
{
    type Error = T::Error;
    fn try_from(r: &'a mut DataReader<'b>) -> Result<Self, Self::Error> {
        let a = String::try_from(r.as_mut())?;
        let b = T::try_from(r.as_mut())?;
        let c = String::try_from(r.as_mut())?;
        Ok(MyData(a, b, c))
    }
}

The Compile error

   Compiling test_borrow v0.1.0 (/root/Projects/test_borrow)
error[E0499]: cannot borrow `*r` as mutable more than once at a time
  --> src/main.rs:56:34
   |
47 | impl <'a, 'b, T> TryFrom<&'a mut DataReader<'b>> for MyData<T>
   |       -- lifetime `'a` defined here
...
55 |         let b = T::try_from(r.as_mut())?;
   |                             ----------
   |                             |
   |                             first mutable borrow occurs here
   |                             argument requires that `*r` is borrowed for `'a`
56 |         let c = String::try_from(r.as_mut())?;
   |                                  ^^^^^^^^^^ second mutable borrow occurs here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `test_borrow` (bin "test_borrow" test) due to previous error

Here in the signature, you promise that if you're going to call T::try_from(), then you're going to pass a &'a mut DataReader<'b> to it. Note the lifetime 'a. Because mutable references are exclusive, this means you are passing your entire ability to use it at all over to T::try_from().

You can do that once, but you cannot do that three times. To see why, imagine a TryFrom impl that just put the input into a struct and returned it; you would then have 3 copies of the same mutable reference, which is not allowed.

You can get this closer to compiling by the HRTB trait bound

T: for<'c> TryFrom<&'c mut DataReader<'b>>,

which says that T::try_from() must accept a mutable reference of any lifetime — but really, this is not a correct use of TryFrom; TryFrom is for data conversions that, if they consume input, consume it wholly, not incremental data reading/parsing. You should create a different trait for this purpose instead, where &mut DataReader<'b> is part of the method signature, not the trait.

4 Likes

Thank you that got it to work but then I could no longer infer the error type from T.
I will take your advice and use a specific Trait for that.

Thank you again