2

Consider the following Rust code, slightly modified from examples in The Book.

I'm trying to understand what happens to the value in the second running of function dangle() in the main() function (see comment). I would imagine that because the value isn't assigned to any owner, it gets deallocated, but I've so far failed to find information to confirm that. Otherwise, I would think that calling dangle() repeatedly would constantly allocate more memory without deallocating it. Which is it?

fn main() {
    // Ownership of dangle()'s return value is passed to the variable `thingamabob`.
    let thingamabob = dangle();

    // No ownership specified. Is the return value deallocated here?
    dangle();

    println!("Ref: {}", thingamabob);
}

fn dangle() -> String {
    // Ownership specified.
    let s = String::from("hello");

    // Ownership is passed to calling function.
    s
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Teekin
  • 12,581
  • 15
  • 55
  • 67

2 Answers2

3

When a value has no owner (is not bound to a variable) it goes out of scope. Values that go out of scope are dropped. Dropping a value frees the resources associated with that value.

Anything less would lead to memory leaks, which would be a poor idea in a programming language.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    More specifically, in your example the second call creates an [anonymous temporary value](https://doc.rust-lang.org/reference/expressions.html#temporary-lifetimes) whose lifetime is just that one line of code, so it goes out of scope immediately. – Dan R Jun 15 '20 at 15:58
  • @DanR I don’t believe that referenced section is relevant to the discussion at hand. – Shepmaster Jun 15 '20 at 16:02
  • 2
    That particular section doesn't seem to apply (an expression statement does not have place expression context), but I think it is worthwhile to point out that the temporary in this case is dropped immediately (i.e. not at the end of `main`). – trent Jun 15 '20 at 16:22
  • @Shepmaster: Thanks, I understand that bit. What was unclear to me was whether the scope ends if the returned value doesn't end up anywhere, but Dan R answered that bit. – Teekin Jun 15 '20 at 20:51
  • 1
    @DanR: Thank you! If you put that bit of info in an answer, I'll mark is as correct. :) – Teekin Jun 15 '20 at 20:52
2

In your example, the second call creates an unnamed temporary value whose lifetime ends immediately after that one line of code, so it goes out of scope (and any resources are reclaimed) immediately.

If you bind the value to a name using let, then its lifetime extends until the end of the current lexical scope (closing curly brace).

You can explore some of this yourself by implementing the Drop trait on a simple type to see when its lifetime ends. Here's a small program I made to play with this (playground):

#[derive(Debug)]
struct Thing {
    val: i32,
}

impl Thing {
    fn new(val: i32) -> Self {
        println!("Creating Thing #{}", val);
        Thing { val }
    }

    fn foo(self, val: i32) -> Self {
        Thing::new(val)
    }
}

impl Drop for Thing {
    fn drop(&mut self) {
        println!("Dropping {:?}", self);
    }
}

pub fn main() {
    let _t1 = Thing::new(1);

    Thing::new(2); // dropped immediately

    {
        let t3 = Thing::new(3);

        Thing::new(4).foo(5).foo(6); // all are dropped, in order, as the next one is created

        println!("Doing something with t3: {:?}", t3);
    } // t3 is dropped here
} // _t1 is dropped last
Dan R
  • 1,412
  • 11
  • 21