Cookie Consent by Free Privacy Policy Generator Aktuallisiere deine Cookie Einstellungen ๐Ÿ“Œ Lifetimes in Rust - one difficult exercise


๐Ÿ“š Lifetimes in Rust - one difficult exercise


๐Ÿ’ก Newskategorie: Programmierung
๐Ÿ”— Quelle: dev.to

The excellent online book Rust By Practice in the Lifetime chapter has a difficult exercise called... A difficult exercise. I struggled with it for a while, then looked at the solution and - I still did not get it. Not until I read the chapter on ownership from The Rustonomicon. Let's dive into the problem and describe the solution in detail.

First we need to go over the code in the exercise and explain what it does. It defines a structure called List; that structure has nothing to do with an actual list, it is just a container for a single object. The object it holds is an instance of a Manager structure. The List gives mutable access to the Manager but not directly - instead it creates container called Interface that holds mutable reference to the Manager. This is done in get_interface method that returns Interface. The only method of Interface is noop(self).

struct Interface<'a> {
    manager: &'a mut Manager<'a>
}

impl<'a> Interface<'a> {
    pub fn noop(self) {
        println!("interface consumed");
    }
}

struct Manager<'a> {
    text: &'a str
}

struct List<'a> {
    manager: Manager<'a>,
}

impl<'a> List<'a> {
    pub fn get_interface(&'a mut self) -> Interface {
        Interface {
            manager: &mut self.manager
        }
    }
}

fn main() {
    let mut list = List {
        manager: Manager {
            text: "hello"
        }
    };

    list.get_interface().noop(); // <--- Interface created and used here.

    println!("Interface should be dropped here and the borrow released");

    use_list(&list);
}

fn use_list(list: &List) {
    println!("{}", list.manager.text);
}

When trying to run this code, rust fails to compile it with the following error:

Compilation error

According to rust's lifetime rules, borrow is valid from it's declaration to it's last use. Last use of Interface is when noop() is called in the main function. It should be similar to this example:

fn main() {
    let mut x = 1;
    let y = &mut x;
    // print!("{x}"); <-- this will not compile, 
    //                    x is mutably borrowed so print cannot borrow it again

    *y = 42; // last use of y

    print!("{x}"); // x no longer borrowed, can be used
}

So why it does not work? Let's analyze reference lifetimes in the main function. I am using similar notation that the The Rustonomicon is using, showing scopes of each lifetime. Note this is not valid rust syntax!

fn main() {
    'a {
        let mut list = List { // list has lifetime 'a
            manager: Manager {
                text: "hello"
            }
        };        

        'b {
            list.get_interface().noop(); // Interface uses lifetime 'b
        }

        // 'b is out of scope
        println!("Interface should be dropped here and the borrow released");

        use_list(&list);
    } // lifetime 'a ends here
}

But... this still does not explain the issue. 'b lifetime is short and ends before use_list is called. To understand what happens we need to look closer at get_interface() method declaration:

impl<'a> List<'a> {
    pub fn get_interface(&'a mut self) -> Interface {
        Interface {
            manager: &mut self.manager // what is lifetime of this?
        }
    }
}

Rust lifetime elision allows us to skip some lifetime parameters. If we make all lifetimes explicit, the declaration will look as below

impl<'a> List<'a> {
    pub fn get_interface(&'a mut self) -> Interface<'a> {
        Interface::<'a> {
            manager: &'a mut self.manager
        }
    }
}

The lifetime 'a from above snippet will be the same as the lifetime 'a from main function since we are calling this method on list object in main. This means Interface.manager will hold reference with 'a lifetime. And if you look inside Manager struct it uses this lifetime in text reference. Recall the implementation of Interface:

struct Interface<'a> {
    manager: &'a mut Manager<'a>
}

Even if we stop using Interface, the manager reference is still alive as it is using 'a lifetime, which in get_interface will be bound to List's lifetime also called 'a here.

How do we fix this? We need to untangle Interface's lifetime from Manager's lifetime. Let's introduce separate lifetime 'b for Interface's reference. I will consistently use 'a and 'b which should make it easier to track which lifetime is which.

struct Interface<'b, 'a> {
    manager: &'b mut Manager<'a> // reference with lifetime 'b
                                 // holding Manager with lifetime 'a
}

impl<'b, 'a> Interface<'b, 'a> {
    pub fn noop(self) {
        println!("interface consumed");
    }
}

We could bind the lifetimes declaring 'a: 'b to indicate 'a outlives 'b, but this is not necessary in this example.

The last thing remaining is to create Interface instance with proper lifetimes. List uses only one lifetime 'a, but we can extend get_interface method to take another generic lifetime parameter 'b.

impl<'a> List<'a> {
    pub fn get_interface<'b>(&'b mut self) -> Interface<'b, 'a> {
        Interface {
            manager: &mut self.manager
        }
    }
}

Now we explicitly told rust to use 'b lifetime for reference inside Interface. Once usage is done, rust can release the reference and list becomes available.

The Rustonomicon describes itself as the awful details that you need to understand when writing Unsafe Rust programs. In this example we did not do any unsafe rust, yet the knowledge from that book was very helpful. I recommend reading The Rustonomicon even if you only plan to play safe.

Sources:

  1. https://doc.rust-lang.org/nomicon/lifetimes.html
  2. https://practice.course.rs/lifetime/advance.html
  3. https://doc.rust-lang.org/nomicon/lifetime-elision.html
...



๐Ÿ“Œ Rust Borrow Checker Explained Part 3: Function Result Lifetimes


๐Ÿ“ˆ 37.11 Punkte

๐Ÿ“Œ Ultimate Guide To Rust Lifetimes For Newbies


๐Ÿ“ˆ 37.11 Punkte

๐Ÿ“Œ since there's none on the web, let reddit make the first least difficult to most difficult ranking of linux oses in 2018


๐Ÿ“ˆ 32.77 Punkte

๐Ÿ“Œ Training a Single AI Model Can Emit As Much Carbon As Five Cars In Their Lifetimes


๐Ÿ“ˆ 28.37 Punkte

๐Ÿ“Œ Web body mulls halving HTTPS cert lifetimes. That screaming in the distance is HTTPS cert sellers fearing orgs will bail for Let's Encrypt


๐Ÿ“ˆ 28.37 Punkte

๐Ÿ“Œ Venafi Media Alert: Certificate Lifetimes Decrease Again, Increasing the Risk of Outages


๐Ÿ“ˆ 28.37 Punkte

๐Ÿ“Œ Unpopular Opinion: Linux will never be popular in our lifetimes.


๐Ÿ“ˆ 28.37 Punkte

๐Ÿ“Œ Rust Tutorial: Learn How To Be Productive In Rust (2018)


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Programmiersprache: Rust 1.31 markiert den Start von Rust 2018


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Medium CVE-2019-12083: Rust-lang RUST


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Rust Survey 2019: Rust-Nutzer wollen besser abgeholt werden


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Experimente mit Rust: Microsoft prรคsentiert Public Preview von Rust/WinRT


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Kubernetes mit Rust: Microsoft setzt fรผr Cloud-Entwicklung auf Rust


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Kubernetes mit Rust: Microsoft setzt fรผr Cloud-Entwicklung auf Rust


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Security: Denial of Service in rust-cbindgen und rust (SUSE)


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Denial of Service in rust-cbindgen und rust (SUSE)


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Announcing Rust 1.45.0 | Rust Blog


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Denial of Service in rust-cbindgen und rust (SUSE)


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ yaml-rust Crate up to 0.4.0 on Rust Deserialization Recursion denial of service


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Rust: Rust-Foundation soll gegrรผndet werden


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Rust 1.31 Released As 'Rust 2018' In Major Push For Backwards Compatibility


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Medium CVE-2020-25792: Rust-lang RUST


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Medium CVE-2020-25794: Rust-lang RUST


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Medium CVE-2020-25791: Rust-lang RUST


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Medium CVE-2021-31162: Rust-lang RUST


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Rust Fuzzing #3: How to write (better) Rust fuzz targets?


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ heise-Angebot: betterCode() Rust: Online-Konferenz โ€“ Dein praxisnaher Einstieg in Rust


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Rust WebAssembly (wasm) on Arch Linux with Webpack (Rust 1.66)


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Rust Basics Series #1: Create and Run Your First Rust Program


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Rust Basics Series #2: Using Variables and Constants in Rust Programs


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Rust Basics Series #3: Data Types in Rust


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Rust Basics Series #4: Arrays and Tuples in Rust


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Rust fรผr Linux: Google fรถrdert die Umstellung des Linux-Kernels auf Rust โ€“ finanziert Entwickler fรผr ein Jahr


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Introduction to the series [1 of 35] | Beginner's Series to: Rust | Beginner's Series to Rust


๐Ÿ“ˆ 17.48 Punkte

๐Ÿ“Œ Digging through Rust to find Gold: Extracting Secrets from Rust Malware


๐Ÿ“ˆ 17.48 Punkte











matomo