rusttraitslifetime

Rust trait cannot borrow as mutable more than once


I have this fairly simple generic Rust code that I cannot find a way to get past the borrow checker. I would like to use a trait in a generic function, pass a mutable reference to a struct that implements Seek + Read to it and then later use that struct once more outside the trait.

Here is a simplified example:

use std::{
    error::Error,
    io::{Read, Seek},
};

trait Foo<'r, T: Seek + Read>: Sized {
    type Item: 'static;
    fn new(reader: &'r mut T) -> Result<Self, Box<dyn Error>>;
    fn next(&mut self) -> Result<Option<Self::Item>, Box<dyn Error>>;
}

fn bar<'r, T: Seek + Read, F: Foo<'r, T>>(
    reader: &'r mut T,
) -> Result<Vec<F::Item>, Box<dyn Error>> {
    let mut parser = F::new(reader)?;
    let item = parser.next()?.unwrap();
    // drop(parser); // does not help
    reader.seek(std::io::SeekFrom::Start(0))?;

    Ok(vec![item])
}

The error message I'm getting is:

error[E0499]: cannot borrow `*reader` as mutable more than once at a time
  --> src/demo.rs:17:5
   |
12 | fn bar<'r, T: Seek + Read, F: Foo<'r, T>>(
   |        -- lifetime `'r` defined here
...
15 |     let mut parser = F::new(reader)?;
   |                      --------------
   |                      |      |
   |                      |      first mutable borrow occurs here
   |                      argument requires that `*reader` is borrowed for `'r`
16 |     let item = parser.next()?.unwrap();
17 |     reader.seek(std::io::SeekFrom::Start(0))?;
   |     ^^^^^^ second mutable borrow occurs here

I have tried various changes of the lifetimes with no success, e.g., fn bar<'p, 'r: 'p, T: Seek + Read, F: Foo<'p, T>>. When I remove the lifetime 'r in the function signature fn new(reader: &'r mut T) the above error goes away but then the implementations of that function get problems because the lifetimes of the reader reference and Foo instances need to be linked. Dropping the Foo object in barbefore attempting to seek doesn't help either.

I would greatly appreciate any pointers to how I could fix this.


I have tried to implement this based on @drewtato's answer and I can get the code to compile. However, when I try to actually call bar, I'm running into another issue.

For this implementation:

pub struct FooA<'r, T: Seek + Read> {
    reader: &'r mut T,
    buf: Vec<u8>,
}

#[derive(Debug, PartialEq)]
pub struct ItemA {
    pub x: f64,
}

impl<'r, T: Seek + Read> Foo<'r, T> for FooA<'r, T> {
    type Item = ItemA;

    fn new(reader: &'r mut T) -> Result<Self, Box<dyn Error>> {
        Ok(FooA {
            reader,
            buf: vec![],
        })
    }

    fn next(&mut self) -> Result<Option<Self::Item>, Box<dyn Error>> {
        let data = self.reader.read_f64::<LittleEndian>().unwrap();
        Ok(Some(Self::Item { x: data }))
    }
}

fn main() {
    let mut cursor = Cursor::new(vec![1, 2, 3, 4, 5, 6, 7, 8]);
    let res: Vec<ItemA> = bar::<Cursor<Vec<u8>>, FooA<'_, Cursor<Vec<u8>>>>(&mut cursor).unwrap();
}

I am getting two errors trying to call bar().

error: implementation of `Foo` is not general enough
   --> src/main.rs:122:27
    |
122 |     let res: Vec<ItemA> = bar::<Cursor<Vec<u8>>, FooA<'_, Cursor<Vec<u8>>>>(&mut cursor).unwrap();
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
    |
    = note: `Foo<'0, std::io::Cursor<Vec<u8>>>` would have to be implemented for the type `FooA<'_, std::io::Cursor<Vec<u8>>>`, for any lifetime `'0`...
    = note: ...but `Foo<'1, std::io::Cursor<Vec<u8>>>` is actually implemented for the type `FooA<'1, std::io::Cursor<Vec<u8>>>`, for some specific lifetime `'1`
error[E0597]: `cursor` does not live long enough
   --> src/main.rs:122:77
    |
121 |     let mut cursor = Cursor::new(vec![1, 2, 3, 4, 5, 6, 7, 8]);
    |         ---------- binding `cursor` declared here
122 |     let res: Vec<ItemA> = bar::<Cursor<Vec<u8>>, FooA<'_, Cursor<Vec<u8>>>>(&mut cursor).unwrap();
    |                           --------------------------------------------------^^^^^^^^^^^-
    |                           |                                                 |
    |                           |                                                 borrowed value does not live long enough
    |                           argument requires that `cursor` is borrowed for `'static`
123 | }
    | - `cursor` dropped here while still borrowed
    |
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
   --> src/main.rs:108:8
    |
108 |     F: for<'r> Foo<'r, T>,
    |        ^^^^^^^^^^^^^^^^^^

I'd be very grateful for any additional pointers.


Solution

  • Your F may capture reader as long as 'r. You can destruct F into reader if you do not need parser afterwards:

    use std::{
        error::Error,
        io::{Read, Seek},
    };
    
    trait Foo<'r, T: Seek + Read>: Sized {
        type Item: 'static;
        fn new(reader: &'r mut T) -> Result<Self, Box<dyn Error>>;
        fn next(&mut self) -> Result<Option<Self::Item>, Box<dyn Error>>;
        // new
        fn into_reader(self) -> &'r mut T;
    }
    
    fn bar<'r, T: Seek + Read, F: Foo<'r, T>>(
        reader: &'r mut T,
    ) -> Result<Vec<F::Item>, Box<dyn Error>> {
        let mut parser: F = F::new(reader)?;
        let item = parser.next()?.unwrap();
        // new
        let reader = parser.into_reader();
        reader.seek(std::io::SeekFrom::Start(0))?;
    
        Ok(vec![item])
    }