0
votes

I am trying to rewrite an algorithm from javascript to rust. In the following code, I get borrowed value does not live long enough error at line number 17.

[dependencies]
scraper = "0.11.0"
use std::fs;

fn get_html(fname: &str) -> String {
    fs::read_to_string(fname).expect("Something went wrong reading the file")
}

pub mod diff_html {
    use scraper::{element_ref::ElementRef, Html};

    pub struct DiffNode<'a> {
        node_ref: ElementRef<'a>,
    }

    impl<'a> DiffNode<'a> {
        fn from_html(html: &str) -> Self {
            let doc = Self::get_doc(&html);
            let root_element = doc.root_element().to_owned();
            let diffn = Self {
                node_ref: root_element,
            };
            diffn
        }

        fn get_doc(html: &str) -> Html {
            Html::parse_document(html).to_owned()
        }
    }

    pub fn diff<'a>(html1: &str, _html2: &str) -> DiffNode<'a> {
        let diff1 = DiffNode::from_html(&html1);
        diff1
    }
}

fn main() {
    //read strins
    let filename1: &str = "test/test1.html";
    let filename2: &str = "test/test2.html";

    let html1: &str = &get_html(filename1);
    let html2: &str = &get_html(filename2);

    let diff1 = diff_html::diff(html1, html2);

    //write html
    //fs::write("test_outs/testx.html", html1).expect("unable to write file");
    //written output file.
}
warning: unused variable: `diff1`
  --> src\main.rs:43:9
   |
43 |     let diff1 = diff_html::diff(html1, html2);
   |         ^^^^^ help: if this is intentional, prefix it with an underscore: `_diff1`
   |
   = note: `#[warn(unused_variables)]` on by default

error[E0597]: `doc` does not live long enough
  --> src\main.rs:17:32
   |
14 |     impl<'a> DiffNode<'a> {
   |          -- lifetime `'a` defined here
...
17 |             let root_element = doc.root_element().to_owned();
   |                                ^^^--------------------------
   |                                |
   |                                borrowed value does not live long enough
   |                                assignment requires that `doc` is borrowed for `'a`
...
22 |         }
   |         - `doc` dropped here while still borrowed

I want a detailed explanation/solution if possible.

1

1 Answers

0
votes

root_element which is actually an ElementRef has reference to objects inside doc, not the actual owned object. The object doc here is created in from_html function and therefore owned by the function. Because doc is not returned, it is dropped / deleted from memory at the end of from_html function block.

ElementRef needs doc, the thing it is referencing to, to be alive when it is returned from the memory.

pub mod diff_html {
    use scraper::{element_ref::ElementRef, Html};

    pub struct DiffNode<'a> {
        node_ref: ElementRef<'a>,
    }

    impl<'a> DiffNode<'a> {
        fn from_html(html: &'a scraper::html::Html) -> Self {
            Self {
                node_ref: html.root_element(),
            }
        }
    }

    pub fn diff<'a>(html1_string: &str, _html2_string: &str) {
        let html1 = Html::parse_document(&html1_string);
        let diff1 = DiffNode::from_html(&html1);

        // do things here

        // at the end of the function, diff1 and html1 is dropped together
        // this way the compiler doesn't yell at you 
    }
}

More or less you need to do something like this with diff function to let the HTML and ElementRef's lifetime to be the same.

This behavior is actually Rust's feature to guard values in memory so that it doesn't leak or reference not referencing the wrong memory address.

Also if you want to feel like operating detachable objects and play with reference (like java, javascript, golang) I suggest reading this https://doc.rust-lang.org/book/ch15-05-interior-mutability.html