How do non-drop variables work?

Heyo. Going through section 4.1 of the book right now, and I'm a little confused.

In Ownership Rules it explains that values are dropped when their owner goes out of scope. The next section, Variable Scope, explains that variables become invalid after coming out of scope.

Just different ways of wording it, alright. That makes sense.

There is a natural point at which we can return the memory our String needs to the allocator: when s goes out of scope. When a variable goes out of scope, Rust calls a special function for us. This function is called drop, and it’s where the author of String can put the code to return the memory. Rust calls drop automatically at the closing curly bracket. - The Memory and Allocation section

Alright, still making sense, but here's where it gets confusing:

Rust won’t let us annotate a type with Copy if the type, or any of its parts, has implemented the Drop trait. If the type needs something special to happen when the value goes out of scope and we add the Copy annotation to that type, we’ll get a compile-time error. - The What is Ownership section

According to them, dropping a variable when it goes out of scope is fundamental- a full on rule for how ownership works. And yet, not all variables have the Drop trait? Obviously, my understanding is flawed. Anyone have insight?

Not implementing Drop just means that the type doesn't run any code provided by that type when it is dropped. It doesn't mean that the type isn't droppable. The Drop trait is arguably poorly named; it's more like InAdditionToRegularDrop. It's also weird as traits go, because

  • It's called if it's implemented, and not called if it isn't — whereas ordinarily either a trait is irrelevant, or not implementing it will result in a compilation error.
  • You can't explicitly call Drop::drop() from Rust code, ever.
3 Likes

It's special in some other ways too. Even if your type doesn't implement Drop, you might include some types in your type which do, and those implementations still need to be called too.

Consider the following struct:

struct S {
    s: String,
}

It has no Drop implementation so Drop::drop is never called. However it has "drop glue" -- when it goes out of scope, a compiler-generated destructor still runs, which drops each field in turn. Otherwise the contained String would no longer be deallocated. This drop glue destructor is part of "being dropped", even though it's not Drop::drop.

That's the "any of its parts" check of Copy (and it's transitive: Consider struct S2(S); ...).

It's also why bounds on Drop (even negative ones, if that was supported) aren't meaningful.

More documentation is on Drop itself.

1 Like

By the way, since this is about confusion with things named “drop”, I think it’s useful to be aware that the std::mem::drop function, which drops its argument, is nothing special or magical at all. It is literally simply implemented as follows:

#[inline]
pub fn drop<T>(_x: T) {}

I.e. the compiler-provided way to drop values in Rust is to have them in a variable that goes out of scope, and std::mem::drop simply accepts an argument _x, does nothing to it, and lets it fall out of scope.

(There is a second way to drop values – useful for only unsafe code – which is also provided by the compiler and doesn’t involved a stack variable going out of scope: it’s called std::ptr::drop_in_place, and its used internally by data structures that manually allocate like e.g. Vec as an opimization in order to drop values on the heap without needing to move them, in particular before deallocation.)

Either way of dropping a value will execute the whole so-called “drop glue”, i.e. Drop::drop implementations for the value’s type and also all its fields recursively. So the other important take-away is that despite the similar naming, calling std::mem::drop(x) does more than just calling the Drop::drop(&mut x) implementation (if it even has one) for x itself.

7 Likes

Thanks for all the answers everyone, really informative!

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.