Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Missing Lifetime Operator

Tags:

rust

lifetime

I have the following code in Rust. I know that I am not supposed to return references to local variables, and in this case I am not. The string to split is passed as a &str reference and, after determining the split boundary, I am returning &s[0..idx] where idx is the end of the boundary. I was confident that this would not result in a "dangling" reference related error. However, it turns out I was wrong!

fn demo4() {
    let mut s = String::from("Elijah Wood");
    let firstname = str_split(&s, &String::from(" "));
    println!("First name of actor: {}", firstname);
}
// can handle both &str and &String
fn str_split(s: &str, pat: &str) -> &str {
    let bytes = s.as_bytes();
    let b_pat = pat.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b_pat {
            return &s[0..i];
        }
    }
    &s[..]
}

fn main() {
    demo4();
}

I am getting the following error:

error[E0106]: missing lifetime specifier
 --> src/main.rs:7:37
  |
7 | fn str_split(s: &str, pat: &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 `s` or `pat`

Any explanation is greatly appreciated.

like image 668
Abrar Hossain Avatar asked Dec 08 '22 16:12

Abrar Hossain


1 Answers

The error message tells you what's wrong, although not how to fix it:

  = help: this function's return type contains a borrowed value, but the
signature does not say whether it is borrowed from `s` or `pat`

The compiler uses lifetimes to determine whether code is safe or not. Part of that is knowing what each reference could be borrowing from. The signature:

fn str_split(s: &str, pat: &str) -> &str

does not indicate whether str_split returns a reference into s or a reference into pat, so Rust can't tell how to check the validity of the reference. (See also this question for a version of this where the function has no reference arguments at all.)

To fix this, you need to introduce a lifetime parameter:

fn str_split<'a>(s: &'a str, pat: &str) -> &'a str

This says, roughly, "If you borrow a string for some lifetime 'a, you can call str_split on it (and another string) and get back a reference also valid for lifetime 'a." &pat is not annotated with 'a, because the result does not borrow from pat, only from s.

The Rust Programming Language has a chapter on lifetimes that addresses this very issue and I would strongly recommend you read it; Rust's lifetimes go beyond merely preventing dangling pointers.


Although not part of the question, the the body of this function is a one-liner. Unless this is purely a learning exercise, don't do more work than you have to:

fn str_split<'a>(s: &'a str, pat: &str) -> &'a str {
    s.split(pat).next().unwrap_or(s)
}
like image 144
trent Avatar answered Jan 05 '23 23:01

trent