Drop Trait in Primitive Types

primitive types like u32 are not implemented Drop trait
.Then how it droped after goes out of scope?

The stack memory occupied by any type, regardless of Drop, is reclaimed as a side effect of a function returning and the stack pointer being adjusted. Primitive types have nothing to clean up besides their stack memory. It's types that have something extra besides just stack memory to clean up that require a Drop implementation.

2 Likes

It's easiest to answer that question if you know how the call stack works.

When your code calls a function, the values passed into the function (including, potentially, pointers to data on the heap) and the function’s local variables get pushed onto the stack. When the function is over, those values get popped off the stack.

Values on the stack which don't have destructors (like u32) are simply discarded as part of popping the call stack.[1] Nothing extra needs to happen to clean them up.


Reality is a bit more complicated than that, though. For example, a value may go out of scope within a function. The compiler keeps track of when this happens and can reuse the stack space that the value took up. When a value goes out of scope, the destructor runs (if it has one) after which the value is invalid and the space can simply be reused. Things like the borrow checker also keep track of when things go out of scope to make sure you don't end up with references to invalidated values.

Additionally, values may actually be put in registers and not on the stack, sometimes values are promoted to static memory as part of compilation, and so on.


If you have a u32 on the heap, then in Rust this generally means you have it in a data structure which is responsible for allocation and deallocation, such as a Vec<u32> or a Box<u32> or a Box<SomeDataStructureContainingTheU32> or so on. The allocating data structures do have destructors, and will take care of dropping values as part of deallocation (or other operations that logically discard their contents, like Vec::clear).


Note that there's not a one-to-one correspondence between having a destructor and having a Drop implementation. Tuples don't have Drop implementations for example, but a (u32, String) needs a destructor in order to recursively call String's destructor.


  1. (The destructors of values which do have destructors are called before discarding them.) ↩︎

4 Likes

String is also doesn't implement drop..Then how the cleaning process works

String stores its contents in a Vec<u8> internally, which does implement Drop.

(The compiler automatically generates so-called "drop glue" for any type that contains other types with a mon-trivial destructor, so such wrapper types don't have to explicitly implement drop to avoid leaking memory.)

3 Likes

The compiler generates the destructors for any types that need them by recursively dropping fields. The compiler-generated destructor is called whether or not you implement Drop. In some places this is called "drop glue".

More documentation.

1 Like

why primitive types(like i32) doesn't implement drop trait?How the cleaning process works?

Primitive types have no external associated resources, they only occupy their own place. There is no need for any "cleaning". When a primitive type goes out of scope, its place can simply be re-used and overwritten once necessary. If it's not necessary, then nothing needs to be done.

2 Likes

Stack allocated or heap allocated is NOT the reason that primitive types does not implement the Drop trait.

Suppose we have struct U8(u8);, a U8 value allocated on stack may invoke its Drop::drop() if implemented.

On the other hand, a u8 value allocated on heap won’t drop(). It’s its containers that drop(), e.g Vec<u8>, Box<u8>, not the u8 elements themselves.

3 Likes