about
04/28/2022
RustBites - LifeCycle
Code Repository Code Examples Rust Bites Repo

Rust Bite - LifeCycle

Creation, drop, order of destruction, move

In this LifeCycle Bite our goal is to understand the processes a Rust object goes through during its life time. The first part of the code we will look at focuses on creating a test type that helps demonstrate what happens as an object is created, used, moved, and goes out of scope. We haven't discussed creation of user-defined types yet - we won't until Rust Bite - Structs. So some of this code won't be completely obvious. But that's O.K., you will get the main ideas, and can come back later for details.
In the Test Code dropdown we declare a TestLifeCycle type:
struct TestLifeCycle { count: u32, }
We want instances of this type to announce their creation, cloning, and destruction. That is effected with the functions:
-  new() -> TestLifeCycle   default constructor provides object with default state
-  new(cnt: u32) -> TestLifeCycle   parameterized constructdor provides object with program supplied state
-  clone(&self) -> TestLifeCycle   clone function, e.g., copy constructor makes a new object that is a copy of cloned
-  drop(&mut self)   drop function, e.g., destructor, releases resources held by object when it goes out of scope
-  value(&self) -> u32  value getter returns object's state
The code, in the left panel in this dropdown, implements these functions for TestLifeCycle, and the code in the right panel exercises tests on instances of this type.
Test Code  
All of the remaining codes are various demonstration tests and their outputs, to show you what happens during the lifecycle of an object in Rust. Demonstrate Creation and Drop is a short test that shows that a Move type, like TestLifeCycle, can be dropped before it leaves its scope of definition by calling std::ops::Drop::drop. The output shows the drop. If you comment out the drop call you will see the drop annunciation after the "leaving demo_drop scope" printout. Drop, like a C++ destructor, returns any resources that the type holds when it goes out of scope. TestLifeCycle doesn't hold any resources on the heap, but became a Move type because it implements the Drop trait, as if it did hold resources that needed to be released. The Drop trait is Rust's mechanism for distinguishing a Move type from a Copy type.
Demonstrate Creation and Drop  
Demonstrate Order of Destruction shows that objects are dropped (destroyed) in the reverse order of their creation. Things are done that way in case there are dependencies between objects - not the case here. The demonstration also shows what happens when we rebind a type, as we did for the _third object. Rust calls this "shadowing". A shadowed object is not accessible after its identifier is bound to a new instance. However, as this demonstration shows, the object isn't dropped when it is shadowed, but waits until the thread of execution leaves the scope where it was defined.
Demonstrate Order of Destruction  
Collection Creation and Drop uses a Vec<TestLifeCycle> to collect several TestLifeCycle objects and show how they are created and destroyed. What this shows is that dropping is a recursive activity. When the vector is dropped each of its elements is dropped. It also shows how a parameterized constructor can provide unique state for each instance. Note that, if we don't explicitly call drop on an instance of a Move type, it will be implicitly dropped when it goes out of scope.
Demonstrate Collecton Creation and Drop  
The Move demo illustrates a move operation:
let tlc1 = TestLifeCycle::param_new(42);
let tlc2 = tlc1;  // move here
There are two important things to note from the output. First, the move did not result in an immediate drop. That didn't happen until tlc2 went out of scope. The second, related, thing to notice is that only one drop occurs. That's because tlc1's resources were moved to tlc2, so no drop was needed for tlc1. This validates Figure 1, from RustBites Data.
Demonstrate Move  
There are two major observation we need to make that are not shown in any of the code: Copy types are not dropped. They have no resources to drop. Endowing a copy type with a drop turns it into a Move type, as we saw with the TestLifeCycle type. Without the Drop implementation TestLifeCycle would be a Copy type. If you create a type that holds a Move type component that makes it a Move type. For example, if we had given TestLifeCycle a String member, perhaps a name, that would have turned it into a Move type even without the Drop implementation. The compiler would have dropped it simply by dropping each of its Move elements, like a name String for TestLifeCycle if it had one. This is similar to C++ member-wise destruction in compiler generated destructors.
  Next Prev Pages Sections About Keys