Should I be calling std::mem::forget?


#1

Wanted to know if I should be calling mem::forget?


#2

No doesn’t look like it. What were you trying to achieve by it? forget on references wouldn’t do anything I guess anyway.


#3

I don’t want display_list to be droped. It lives in the main function, and is later passed to another function from the main loop. So I just don’t want display_list to be droped so I can still read from it.


#4

Well in this funtion display_list is just a reference to the vector so the actual vector wont be dropped when the reference goes out of scope.


#5

Ah okay. I thought the value the reference points at would be droped once the reference goes out of scope Thanks for the help


#6

BTW, mem::forget() is dangerous. It’s very easy to get it wrong. Rust can’t help you use it safely, and you may be getting nasty use-after-free bugs from it.

If you need to pass some memory to C, then use Box::into_raw(). That gives you a pointer that is guaranteed to live under a stable address, and the worst that can happen is a memory leak.


#7

Really curious: How can one get use after free errors with it? It just leaks memory, no?


#8

It’s safe only if you never use any pointers to the forgotten memory. However, typical pattern is to take a pointer to the memory, and then “forget” it. That’s playing with fire.

mem::forget doesn’t prevent memory from being freed! It prevents Drop, but that’s an entirely different thing. Partial moves and stack-allocated objects will still be freed after mem::forget!

fn send_foo_to_c() {
   let foo = Foo::new();
   give_pointer_to_c(&foo as *const Foo);
   mem::forget(foo); // it does *not* keep Foo's memory alive!
}

fn main() {
    send_foo_to_c();
    make_c_use_foo(); // C will crash now!
}

or


struct Foo {
    bar: Bar,
}
struct Bar(u32);

{
    let foo = Box::new(Foo {bar: Bar(1)});
    std::mem::forget(foo.bar); // it doesn't do anything
}
// No memory has been leaked. foo.bar is freed now.

Second, owned objects in Rust don’t have stable addresses (References do. Box and smart pointers do, unless you move values out of them.)

When values are moved, Rust can literally memmove them in memory, invalidating all your pointers. Theoretically any assignment or passing of an object into a function, struct, tuple or match can move it. If you do anything between between taking an address and mem::forget, you may accidentally make the pointer invalid, with no warning. Optimizer generally avoids moves, so your code may happen to work only thanks to an optimization, and start crashing when optimizer changes.


#9

Notice that the first thing that happens is that you move the value as the argument to mem::forget! This is not some compiler intrinsic, just a regular function call. Its only magic lies within to prevent a Drop, IIRC just by wrapping it in a ManuallyDrop.

So mem::forget can’t help preserve things that live on the stack, but it can preserve things that are indirectly owned, like the contents of a Box. The immediate “value” is a pointer on the stack, which will be moved into the mem::forget call. But if you don’t Drop a Box, its heap-allocated content won’t be freed.