bool3max
bool3max

Reputation: 2865

Confusion around lifetimes and struct definitions

(I am having trouble grasping lifetimes and ownership in Rust)

Consider the following Rust struct definition:

struct MyType<'lifetime> {
    one: &'lifetime u32,
    two: &'lifetime u32
}

My current interpretation of the above definition is as follows:

I have defined a struct MyType. The <'lifetime> next to its identifier refers to a specific lifetime of any one instance of MyType. The fields one and two are references to u32s, and because they are annotated with 'lifetime, the integers that they refer to are guaranteed to live at least as long as the instance of the struct they belong to.

To challenge my current understanding I created this example:

fn main() {

    let num1: u32 = 11;

    let instance: MyType;

    { // nested scope
        let num2: u32 = 22;
        instance = MyType {
            one: &num1,
            two: &num2
        };

        println!("{:?}", instance);
    }
}

This code compiles and runs (printing MyType { one: 11, two: 22 }), but it seems to disprove my understanding:

As instance gets dropped at the end of main1 and num2 gets dropped at the end of the nested scope right before it, my initial understanding seems false: the integer referred to by the two field didn't live "at least as long as the struct instance".

Where am I wrong? What does the 'lifetime in struct MyType<'lifetime'> actually refer to?


1: I am also unsure if this is correct.

Upvotes: 2

Views: 149

Answers (2)

bool3max
bool3max

Reputation: 2865

My initial understanding (documented in the quote block in the question) of what <'lifetime> represents in the struct MyType<'lifetime> definition is wrong.

<'lifetime> in the definition of the struct represents a generic type parameter, denoting that MyType contains a reference that borrows something with the 'lifetime lifetime - it does not represent the lifetime of any given instance of the MyType struct.

Upvotes: 0

cdhowie
cdhowie

Reputation: 169478

instance gets dropped at the end of main

With the code you've written, this is incorrect. You've discovered non-lexical lifetimes (NLL).

In Rust, a value is only guaranteed to live until the end of its enclosing block if it implements the Drop trait. Otherwise, the compiler is free to end its lifetime when it is no longer used. Since you do not use instance after num2 goes out of scope, this code is acceptable; the lifetime of instance is cut short after its last usage in the println!() call, at which point num2 still exists.

There's a few ways you can make this code fail.

One is to move the println!() call outside of the anonymous block:

fn main() {
    let num1: u32 = 11;

    let instance: MyType;

    { // nested scope
        let num2: u32 = 22;
        instance = MyType {
            one: &num1,
            two: &num2
        };
    }

    // error[E0597]: `num2` does not live long enough
    println!("{:?}", instance);
}

The other is to implement Drop on MyType, which disables NLL for values of that type.

impl Drop for MyType<'_> {
    fn drop(&mut self) { }
}

This will produce:

error[E0597]: `num2` does not live long enough
...
borrow might be used here, when `instance` is dropped and runs the `Drop` code for type `MyType`

Upvotes: 4

Related Questions