Rust lifetimes, a high wall for Rust newbies
Tags: rust
Published: 2019-01-03 02:27:26

---
title: Rust lifetimes, a high wall for Rust newbies
published: true
description: just summurlizing what I currently learned
tags: Rust 
---

# Learning Rust is hard

I recently start learning Rust. The language is famous but at the same time is infamous. Everybody says it has steep learning curves and some concepts (mostly borrowing, ownership and lifetimes) are hard to grasp. 

Let me explain some of the concepts related to lifetime. (Don't assume me as an expert of lifetime, even though I deciced to write this article, I feel I still don't get the concept. Also, this article is just a paraphase of the book and the nomicon.)

If you want to run your code on your machine, run it with Rust 1.31.1, current (2019 Jan) stalbe.

Most of the codes are copied from [the book](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html), [the nomicon](https://doc.rust-lang.org/nomicon/lifetimes.html).

# Lifetime Basics

This does not compile, but

```rust
fn main() {
    {
        let r;

        {
            let x = 5;
            r = &x;
        }

        println!("r: {}", r);
    }
}
```

This does compile
```rust
fn main() {
    let r;

    let x = 5;
    r = &x;

    println!("r: {}", r);
}
```

Why? Rust's variables live within their scope (between "{" and "}"). If they go out boundries, compiler kills them, just like a prison. 

The problem of the first code is var r. r has a borrowed value from x and we ask Rust to print out on final line. Notice that x dies soon after he loaned his value to r. So, on final line, we basically trying to priting out a value which does not exists. Rust yells ERROR.

The solution is simple. Just remove the scope and let r an x live til the end of main func.

Rust decides how long each vars live, according to nomicon.

This simple code

```rust

fn main() {
    let x = 0;
    let y = &x;
    let z = &y;
}

```

is just a sugared one. If we desugar, it looks like this.

```rust
fn main() {
    'a: {
        let x: i32 = 0;
        'b: {
            let y: &'b i32 = &'b x;
            'c: {
                // ditto on 'c
                let z: &'c &'b i32 = &'c y;
            }
        }
    }
}

```

The nomicon says lifetimes are just a regions. Yes, it looks like code blocks.

(Unfortunately, lifetime boundries are not visualized and there's no way to do it. I wish if it were. [Nashenas88](https://internals.rust-lang.org/t/borrow-visualizer-for-the-rust-language-service/4187/30?u=nashenas88) is working on the visualization project. Good Luck!!)

# Function and Lifetime

This code does not compile

```rust
#[allow(unused_variables)]
fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
}


fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

```

Rust says...

```
error[E0106]: missing lifetime specifier
  --> src/main.rs:11:33
   |
11 | fn longest(x: &str, y: &str) -> &str {
   |                                 ^ expected lifetime parameter
   |
   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
```

The longest function returns either x or y. Nobody knows exactly which. 

In most cases Rust tries to assume var's lifetimes based on simple rules (it's called lifetime elission). In this case, the rules does not apply. So we need to add lifetimes manually, let's do it.

This compiles

```rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
```

How about giving different lifetimes for x and y? 

That won't work, since the function made to return only 1 result. The result comes with 1 lifetime, but which lifetime, x's or y's (we can't say either or both)? 

# Lifetime subtypes

Let's try another example. 
In this example, I strucutrued that Parser contains Context object and parse_context does parsing job.

```rust
struct Context(&str);

struct Parser {
    context: &Context,
}

impl Parser {
    fn parse(&self) -> Result<(), &str> {
        Err(&self.context.0[1..])
    }
}

fn parse_context(context: Context) -> Result<(), &str> {
    Parser { context: &context }.parse()
}
```

If we compile, we'll get error.


```
error[E0106]: missing lifetime specifier
 --> src/lib.rs:1:16
  |
1 | struct Context(&str);
  |                ^ expected lifetime parameter

error[E0106]: missing lifetime specifier
 --> src/lib.rs:4:14
  |
4 |     context: &Context,
  |              ^ expected lifetime parameter

error[E0106]: missing lifetime specifier
  --> src/lib.rs:13:50
   |
13 | fn parse_context(context: Context) -> Result<(), &str> {
   |                                                  ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
   |
   = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments

```

Let's add a lifetime.

```rust
struct Context<'a>(&'a str);

struct Parser<'a> {
    context: &'a Context<'a>,
}

impl<'a> Parser<'a> {
    fn parse(&self) -> Result<(), &'a str> {
        Err(&self.context.0[1..])
    }
}

fn parse_context<'a>(context: Context<'a>) -> Result<(), &'a str> {
    Parser { context: &context }.parse()
}

```

and we'll get error....

```
error[E0515]: cannot return value referencing function parameter `context`
  --> src/lib.rs:14:5
   |
14 |     Parser { context: &context }.parse()
   |     ^^^^^^^^^^^^^^^^^^--------^^^^^^^^^^
   |     |                 |
   |     |                 `context` is borrowed here
   |     returns a value referencing data owned by the current function
```

The var context is only available in parse_context function. So, at the end of function, Parser will reffer to a var which does not exist.


What should we do? 

We need 2 lifetimes.
This will compile (only on 1.31.1, not other versions.)

```rust
#![allow(dead_code)]

struct Context<'s>(&'s str);

struct Parser<'c, 's> {
    context: &'c Context<'s>,
}

impl<'c, 's> Parser<'c, 's> {
    fn parse(&self) -> Result<(), &'s str> {
        Err(&self.context.0[1..])
    }
}

fn parse_context(context: Context) -> Result<(), &str> {
    Parser { context: &context }.parse()
}

```


Parser contains Context and Parser reffers Context through "parse" method.

This means that Context has to live longer than Parser. So, we should at least tell Rust that both lifetimes are different.

(Although I'm writing "we need 2 lifetimes", I still don't get why we need 2 lifetims. Single lifetime means Context and Parser dies at the same time. What's the problem!)

(Do you think reading [RFC for non-lexical lifetimes](https://github.com/rust-lang/rfcs/blob/master/text/2094-nll.md) would be good for understanding lifetimes, although it's super lengthy? )