3

I have the code:

struct Foo {}

impl Default for Foo {
    fn default() -> Self {
        Self {}
    }
}

impl Drop for Foo {
    fn drop(&mut self) {
        // Do something
    }
}

fn main() {
    {
        let foo = Some(Foo::default());
        let foo = None; // Would this line trigger `Foo::drop`?
    };
    {
        let mut foo = Some(Foo::default());
        foo = None; // Would this line trigger `Foo::drop`?
    };
}

Are the resources occupied by foos released properly?

The first situation (variable overwritten) will not trigger drop, so I added the second situation, in which I'm also confused.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Galaxy
  • 1,129
  • 11
  • 27

2 Answers2

4
let foo = Some(Foo::default());
let foo = None; // Would this line trigger `Foo::drop`?

No, the end of the block will.

Remember that let is a variable declaration, so the second line is not modifying the first foo, it is creating a new foo that shadows the former. But it still exists (and could be accessed if you created a reference to it before the second let) until the end of the block, so it will be dropped at end of the block.

If you want to actually change the variable, you have to do

let mut foo = Some(Foo::default());
foo = None;

Now it will trigger the drop immediately, because the old value is being overwritten, so it has to drop it.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
  • In a hypothetical scenario, if you keep shadowing a variable like that in a loop (and loop is very long) does that have potential to "leak" quite a bit of memory until that end of block is reached? – Logan Reed Jun 10 '19 at 14:48
  • @LoganReed You can't shadow a variable created in a loop in the loop itself -- you end up only shadowing the outer variable each iteration; see [this answer](https://stackoverflow.com/a/40323730/3650362) for example. – trent Jun 10 '19 at 14:59
  • @LoganReed, the body of the loop is a block, so scope of any variable introduced in the loop ends at the end of iteration. – Jan Hudec Jun 10 '19 at 15:50
  • 1
    @trentcl, well you can have two `let foo`s in the body shadowing each other within one iteration—the point is their scope ends at the end of the iteration, so they don't accumulate. – Jan Hudec Jun 10 '19 at 15:52
  • @JanHudec - you are correct. To refine the question - is it dropped (and memory freed) at the end of each loop then, or do those shadowed variables stay on stack till the function ends (i.e. closing brace of the function, not the loop(!))? Rust docs say that stack gets freed only when function exits... If that is true - one can accumulate a lot of shadowed and no longer accessible variables on the stack. – Logan Reed Jun 17 '19 at 13:32
  • @LoganReed, at the end of the loop body in each iteration. The only way things can accumulate on the stack is recursive function calls. – Jan Hudec Jun 17 '19 at 18:41
2

Unless you explicitly notate your variable name as _ it is dropped when the scope has finished.

In your case, variables will be released when the relevant scope is ended.

Since you implement Drop trait for Foo explicitly. It will override the drop behavior and call your drop function:

fn main() {
    let _: Option<Foo> = Some(Foo::default()); // Since variable name is '_' it will be dropped automatically.

    {
        let foo: Option<Foo> = Some(Foo::default());
        let foo: Option<Foo> = None;
        println!("Inner Scope Finishing. Going to drop inner scope variables.");
    } // Inner scope is finished dropping the inner scope variables.

    let main_foo: Option<Foo> = Some(Foo::default());
    let main_foo: Option<Foo> = None;
    println!("Main Scope Finishing. Going to drop main scope variables.");
} // Main is finished dropping the main scope variables

Playground to show the behavior.

Akiner Alkan
  • 6,145
  • 3
  • 32
  • 68