ryosky
ryosky

Reputation: 3

Rust lifetime - "does not live long enough" error happens when I create instance

I'm writing a toy OS. I can't fix following lifetime error.

My code fails to compile due to does not live long enough

error[E0597]: `fb_config` does not live long enough
  --> src/main.rs:37:45
   |
26 | pub extern "sysv64" fn kernel_main (mut fb_config: FrameBufferConfig) -> ! {
   |                                     ------------- has type `FrameBufferConfig<'1>`
...
37 |     let mut pixel_writer = PixelWriter::new(&mut fb_config);
   |                            -----------------^^^^^^^^^^^^^^-
   |                            |                |
   |                            |                borrowed value does not live long enough
   |                            argument requires that `fb_config` is borrowed for `'1`
...
66 | }
   | - `fb_config` dropped here while still borrowed

This is a part of my kernel code. FrameBufferConfig has lifetime and was mutable borrowed to PixelWriter::new(). PixelWriter also has a lifetime and it has the passed FrameBufferConfig as a member.

// src/main.rs

pub extern "sysv64" fn kernel_main (mut fb_config: FrameBufferConfig) -> ! {
    let mut pixel_writer = PixelWriter::new(&mut fb_config);
    ...
}
pub struct FrameBufferConfig<'a> {
    pub frame_buffer: FrameBuffer<'a>,   // https://docs.rs/uefi/0.3.2/uefi/proto/console/gop/struct.FrameBuffer.html
...
}
pub struct PixelWriter<'a> {
    fb_config: &'a mut FrameBufferConfig<'a>,
    pixel_writer: unsafe fn(&mut FrameBuffer, usize, Rgb),
}

impl<'a> PixelWriter<'a> {
    pub fn new(fb_config: &'a mut FrameBufferConfig<'a>) -> Self {
        ...
        PixelWriter {
            fb_config,
            pixel_writer,
        }
    }

IMHO, PixelWriter should live longer than FrameBufferCOnfig. It looks both fb_config (defined at an argument of kernel_main) and PixelWriter (defined in a kernel_main) live and drop until the end of kernel_main. So I think fb_config lives enough.

To fix it, I tried following 2 approaches.

  1. limit PixelWriter's lifetime by block. but it failed. we get the same error.
pub extern "sysv64" fn kernel_main (mut fb_config: FrameBufferConfig) -> ! {
    {
        let mut pixel_writer = PixelWriter::new(&mut fb_config);
    }  // PixelWriter dropped?
    ...
}  // fb_config dropped?
  1. move owner to local value. it passes the compile. but it is redundant.
pub extern "sysv64" fn kernel_main (mut fb_config: FrameBufferConfig) -> ! {
    let mut tmp = fb_config;   // move owner here.
    let mut pixel_writer = PixelWriter::new(&mut tmp);
    ...
}

Do you have any idea why this error happens? Since I'm a beginner of rust, Do I make a mistake of lifetime? Thank you for your help!

Upvotes: 0

Views: 935

Answers (1)

Kevin Reid
Kevin Reid

Reputation: 43753

    fb_config: &'a mut FrameBufferConfig<'a>,

You have over-constrained this lifetime.

  • &'a mut FrameBufferConfig means that 'a specifies how long this FrameBufferConfig is borrowed for — the FrameBufferConfig will be borrowed for at least 'a.
  • FrameBufferConfig<'a> means that 'a specifies the lifetime of references inside of the FrameBufferConfig — so FrameBufferConfig<'a> cannot outlive 'a.

Putting these two together requires the compiler to come to the conclusion that after PixelWriter::new(&mut tmp) is executed, the FrameBufferConfig is borrowed for the rest of its existence (because if 'a ends then FrameBufferConfig<'a> is no longer valid, but the FrameBufferConfig can't stop being borrowed until 'a ends), and therefore can never be dropped or used in any further way.

In general, when mutable references are involved, you will have more need to use distinct lifetimes (and &'a mut Foo<'a> is almost always useless). A simple principle (not optimal, but a place to start) is to either use a separate lifetime for each &mut involved, or use a separate lifetime for all &mut involved. (Which way to go depends on the situation.) In your case, we just need to have two lifetimes for the two roles:

pub struct PixelWriter<'fb, 'gop> {
    fb_config: &'fb mut FrameBufferConfig<'gop>,
    ...

(I took the lifetime name 'gop from the library documentation for consistency.)

It is also possible (depending on your program's needs) that PixelWriter should own the fb_config instead of having a mutable reference to it, i.e.

pub struct PixelWriter<'gop> {
    fb_config: FrameBufferConfig<'gop>,
    ...

But whether that is appropriate or not will depend on what your program's needs are. In particular, if you are going to create multiple PixelWriters sequentially from a single FrameBufferConfig, a reference may be appropriate, but if you are going to create only one for each FrameBufferConfig then ownership simplifies things.

Upvotes: 0

Related Questions