Lifetimes

Every variable, structure attribute, and constant has a lifetime in Rust. Most of them can be elided, since we usually know that a constant has a static lifetime (it will always be there for us), or that most of the variables have the lifetime of its scope. Nevertheless, there is sometimes a place where we need to specify that lifetime. Let's check the following structures:

struct Parent<'p> {
age: u8,
child: Option<&'p Child>,
}

struct Child {
age: u8,
}

As you can see, the parent has a reference to the child, but we added two letters preceded by a single quote. These are lifetime specifications, and what means is that the reference to the child has to live at least while the parent exists. Let's see this behavior with a simple main() function:

fn main() {
let child = Child { age: 10 };
let parent = Parent {
age: 35,
child: Some(&child),
};

println!("Child's age: {} years.", parent.child.unwrap().age);
}

This will print that the child is 10 years old. The child gets dropped at the end of the main function, so the reference is valid while the parent exists. But let's create a small inner scope to see if we can trick the compiler. Inner scopes are explicit scopes that you can create by using curly braces. All variables defined in there will be dropped at the end of the inner scope, and if an expression is added at the end without a semicolon, it will be the value of the scope, and can be assigned to any variable.

Let's try to add a child to a parent that will be dropped at an inner scope:

    let mut parent = Parent {
age: 35,
child: None,
};

{
let child = Child { age: 10 };
parent.child = Some(&child);
}

println!("Child's age: {} years.", parent.child.unwrap().age);

If we try to compile this, the compiler will tell us that child does not live long enough. The compiler has understood that we told it in the structure that child had to live at least as long as the Parent structure, and since, in this case, it knows that the variable defined inside the inner scope will be dropped there, it will complain at compile time and not let you add it to the parent.

This can be extended to functions. Let's consider a very simple function that returns a reference to the oldest child of the two provided:

fn oldest_child(child1: &Child, child2: &Child) -> &Child {
if child1.age > child2.age {
child1
} else {
child2
}
}

This will not compile, since it needs a lifetime parameter. This means that the compiler does not know whether the return child will live as long as child1 or as long as child2. We don't know either, so we will specify that all lifetimes must be at least as long as the current function, and then the rest is the problem of our caller:

fn oldest_child<'f>(child1: &'f Child, child2: &'f Child) -> &'f Child {
if child1.age > child2.age {
child1
} else {
child2
}
}

This just declares a new lifetime (declared before the first parenthesis before the arguments) that we call f, that will be the lifetime of the function. We then specify that all references must live at least as long as the function.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.14.132