Newbie question: Vec::leak without allocating a box?

Hi, I'm doing embedded development using the Rust language. I'm sorta a newbie to Rust, the lifetime system kinda confuses me atm. I'm writing an UEFI bootloader, and I have my own boot protocol for booting kernels. It uses a tag system, consisting of a reference to a slice of tag enum variants. I push the tag values after exiting the boot services, then give the slice reference to a structure (so I can't make any allocations). However, the compiler complains with the following message:

error[E0597]: `memory_map_entries` does not live long enough
   --> src/main.rs:223:52
    |
223 |         tags.push(TagType::MemoryMap(memory_map_entries.as_slice()));
    |                                                    ^^^^^^^^^^^^^^^^^^-----------
    |                                                    |
    |                                                    borrowed value does not live long enough
    |                                                    argument requires that `memory_map_entries` is borrowed for `'static`
...
234 |     } else {
    |     - `memory_map_entries` dropped here while still borrowed

error[E0597]: `tags` does not live long enough
   --> src/main.rs:226:26
    |
226 |         explosion.tags = tags.as_slice();
    |                          ^^^^-----------
    |                          |
    |                          borrowed value does not live long enough
    |                          argument requires that `tags` is borrowed for `'static`
...
234 |     } else {
    |     - `tags` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.

I don't understand how to leak a Vec without allocating a box
Here is reference code for what I'm doing

// This code is inside an if let statement which is used to check if the elf is 64-bit
let mut boot_data = Box::new(BootProtoData::new(Default::default()));
let mut tags = Vec::<TagType>::with_capacity(3);
// [...]
let mut mmap_buf = vec![0; system_table.boot_services().memory_map_size()];
let mut memory_map_entries = Vec::with_capacity(
    mmap_buf.capacity() / size_of::<uefi::table::boot::MemoryDescriptor>(),
);
mmap_buf.resize(system_table.boot_services().memory_map_size(), 0);
// Can't make allocations after here
for desc in system_table
    .exit_boot_services(image, &mut mmap_buf)
    .expect_success("Failed to exit boot services.")
    .1
{
    match desc.ty {
        // Here, depending on the memory type, I call memory_map_entries.push();
    }
}
tags.push(TagType::MemoryMap(memory_map_entries.as_slice()));
boot_data.tags = tags.as_slice();
// Then I call the kernel entry point, passing Box::leak(boot_data) as the argument

BootProtoData::tags is of type &'a [TagType<'a>]
TagType<'a> contains enum variant MemoryMap(&'a [MemoryEntry])
Also, I can't use the alloc crate in the kernel I'm booting yet to use types like Box and Vec in the structures because it's currently very bare-bones.

How about the Vec::leak method?

As per the title, Vec::leak allocates a Box, and I can't make allocations there

That's troublesome, it really shouldn't. As a workaround, you can construct the slice manually (unsafe { slice::from_raw_parts_mut(v.as_mut_ptr(), v.len()) }) and then mem::forget the vector.

If you have a Vec, you have an allocator, so I don't understand what you're actually asking for.

1 Like

I'm working in an UEFI environment, after exiting the boot services, I am unable to make any more allocations, I'd have to set up a different allocator

It will try to shrink your allocation if your capacity is greater than your length (which could be a reallocation depending on the allocator). Outside of that, a new allocation for the box does not occur; the Box is constructed from raw parts.

2 Likes

Oh, actually, this is what is happening with the memory map Vec. I'm discarding a bunch of entries, but allocating a big enough vector for all of the memory map's entries.

If you don't want to do it "manually" without shrinking, you could refactor things so you know you won't shrink -- Option<NonNull<T>> or something so you can keep the vector at capacity. You'd probably want a new type to replace push. Once you end up with a static slice you could shorten the slice.

That works. Thanks everyone. Though, I'm encountering a different issue now (some corruption is happening and I don't know why), I guess I should make a different topic for that.

I submitted a PR to make Vec::leak avoid allocation: Avoid allocations and copying in Vec::leak by mbrubeck · Pull Request #89337 · rust-lang/rust · GitHub

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.