Embedded Rust - borrowed value does not live long enough

For some reason, when I add SdCardReader::new(&mut my_file);, rust will start frowning errors about lifetime. I still can't wrap my head around lifetimes (or at least good enough to solve this issue myself), so here I'm asking for help ;D

main.rs:

use embedded_sdmmc::*;

[...]

type SimpleFile<'a> = &'a mut File<'a, SdCard<ExclusiveDevice<esp_hal::spi::master::Spi<'a, Blocking>, Output<'a>, Delay>, Delay>, SdMmcClock, 4, 4, 1>;

struct SdCardReader<'a> {
    file: SimpleFile<'a>,
    buffer: [u8; 32], // Fixed-size buffer for stb_vorbis
    buffer_pos: usize,
    buffer_size: usize,
    eof: bool,
}

impl<'a> SdCardReader<'a> {
    fn new(file: SimpleFile<'a>) -> Self {

        Self {
            file: file,
            buffer: [0u8; 32],
            buffer_pos: 0,
            buffer_size: 0,
            eof: false,
        }
    }
}

[...]

fn main() -> ! {
    [...]

    let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, time_source);

    let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();
    let mut root_dir = volume0.open_root_dir().unwrap();
    let mut my_file = root_dir.open_file_in_dir("1mb.mp3", embedded_sdmmc::Mode::ReadOnly).unwrap();

    SdCardReader::new(&mut my_file);

    [...]
}

Error Messages:

$ cargo run --release
[...]
error[E0597]: `volume0` does not live long enough
   --> src/main.rs:174:24
    |
173 |     let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();
    |         ----------- binding `volume0` declared here
174 |     let mut root_dir = volume0.open_root_dir().unwrap();
    |                        ^^^^^^^ borrowed value does not live long enough
...
223 | }
    | -
    | |
    | `volume0` dropped here while still borrowed
    | borrow might be used here, when `volume0` is dropped and runs the `Drop` code for type `Volume`

error[E0597]: `root_dir` does not live long enough
   --> src/main.rs:175:23
    |
174 |     let mut root_dir = volume0.open_root_dir().unwrap();
    |         ------------ binding `root_dir` declared here
175 |     let mut my_file = root_dir.open_file_in_dir("1mb.mp3", embedded_sdmmc::Mode::ReadO...
    |                       ^^^^^^^^ borrowed value does not live long enough
...
223 | }
    | -
    | |
    | `root_dir` dropped here while still borrowed
    | borrow might be used here, when `root_dir` is dropped and runs the `Drop` code for type `Directory`

error[E0597]: `my_file` does not live long enough
   --> src/main.rs:177:23
    |
175 |     let mut my_file = root_dir.open_file_in_dir("1mb.mp3", embedded_sdmmc::Mode::ReadO...
    |         ----------- binding `my_file` declared here
176 |
177 |     SdCardReader::new(&mut my_file);
    |                       ^^^^^^^^^^^^ borrowed value does not live long enough
...
223 | }
    | -
    | |
    | `my_file` dropped here while still borrowed
    | borrow might be used here, when `my_file` is dropped and runs the `Drop` code for type `File`
[...]

Mind that I can't use std, because I deploy this code on an ESP32.

This looks very suspicious.

&'a mut File<'a, ...> is not what you have at:

This is &'short mut File<'long, ...>. They are different lifetimes. You are literally creating the short borrow right there before passing it to the constructor. The longer borrow already exists from previous assignments.

You might be able to get this to work if you provide more than one lifetime to rule them all. You will likely also have to specify the relationship between all of the lifetimes at some point.

I should probably also point out that &'a mut File<'a> is Borrowing something forever - Learning Rust

1 Like

In particular, &'a mut Thing<'a> is never what you want. Trying multiple lifetimes is one approach. Another would be to try owning the File<'a, ...> in SdCardReader<'a>.

2 Likes

The thing is after I dropped &mut I still get the same error message

Try naming each lifetime separately, to start with.

I can't see what Spi<'_, Blocking> or Origin<'_> are borrowing, because code for them was omitted. The best I can suggest is giving them different names in your type alias, and warn (again) that you may have to specify relationships between them.

It's better if you reason about them and pick meaningful names so that you don't have to use trial-and-error.

Thank you very much for your help :smile:

Now, I have much bigger mindfuck, then when I started doing this shit. I don't understand lifetimes even more now, but what's important is that my code finally works :melting_face:

Doing some refactoring, I "removed" one lifetime.

-type SimpleFile<'a> = File<'a, SdCard<ExclusiveDevice<esp_hal::spi::master::Spi<'a, Blocking>, Output<'a>, Delay>, Delay>, SdMmcClock, 4, 4, 1>;
+type SimpleFile<'a> = File<'a, SdCard<ExclusiveDevice<esp_hal::spi::master::Spi<'a, Blocking>, DummyPin, Delay>, Delay>, SdMmcClock, 4, 4, 1>;

Then it got to me, that File lifetime could be different from Spis. So, I separated them and it started working.

type SimpleFile<'a, 'b> = File<'b, SdCard<ExclusiveDevice<esp_hal::spi::master::Spi<'a, Blocking>, DummyPin, Delay>, Delay>, SdMmcClock, 4, 4, 1>;