J. Dough
J. Dough

Reputation: 107

Why do I need these lifetime but not others, how to know the correct signature and how can I identify the parameters needing lifetimes?

This is my first bit of Rust code. I added the lifetime parameters to the inner function only because the compiler told me to. Although I understand the lifetime explanation in the Rust book, I couldn't have written this signature by myself.

fn transform_the_expression() {
    fn create_formula<'t>(formula: & mut HashMap<& str, &'t str>, input: &'t str, re: Regex)->std::borrow::Cow<'t, str>{
        let replacement = re.find(input).unwrap();
        formula.insert("1", replacement.as_str());
        let rest = re.replace(input, "1");
        return rest;
    }

    let input = "(a+(b*c))";
    use regex::Regex;
    let re = Regex::new(r"\([\w\d][/*+-^][\w\d]\)").unwrap();
    use std::collections::HashMap;
    let mut formula = HashMap::new();

    let result = create_formula(&mut formula, input, re);

    println!("result = {:?}", result);

}

Upvotes: 0

Views: 126

Answers (1)

Chris Emerson
Chris Emerson

Reputation: 14051

The compiler's great at telling you when you need to specify lifetimes, but not necessarily so good at letting you know which ones.

First of all, it's worth mentioning that there are lifetimes on every reference; it's just that there are lifetime elision rules which say how the compiler will fill them in if you don't specify them.

Back to your signature:

fn create_formula(formula: & mut HashMap<& str, &str>, input: &str, re: Regex)->std::borrow::Cow<_, str>

The first lifetime definitely needed is the one in Cow<_, str>; you need to have a lifetime to put into there to declare the function. There are two choices: declare one, or use 'static. In this case, the Cow may point at part of the input parameter, so you need to tie them together. First the new lifetime needs declaring by adding <'t> (any name would do) after the function name, and then using it for input and the return type:

fn create_formula<'t>(formula: & mut HashMap<& str, &str>, input: &'t str, re: Regex)->std::borrow::Cow<'t, str>

And finally you're putting borrowed parts of input into the HashMap. If the input lifetime (which we've now named 't) isn't tied to the HashMap value, someone could pass in a hash map which lives longer than the input, leading to dangling pointers in the map. So we need to constrain it too, giving the final version:

fn create_formula<'t>(formula: & mut HashMap<&str, &'t str>, input: &'t str, re: Regex)->std::borrow::Cow<'t, str>

The key thing is that function signatures need to convince the compiler that all the uses of references are safe, and that often means you have to be explicit about the relationships between different reference parameters (and lifetime-parametric types) when the default (via the elision rules) don't say the right thing.

Upvotes: 4

Related Questions