I'm new to rust and i'm building an implementation of the grep command to learn this language.
I'm trying to create a function to match the search query in a case insensitive way, but i'm having trouble handling the lifetime of the value returned by str.lines()
iterator in the for loop.
this is a minimal reproducible example containing the function i'm implementing:
use grep::{run, Config};
use std::{env::args, process};
pub struct Config {
file_path: String,
query: String,
}
fn main() {
let contents: Config =Config {query: "Hello world!".to_owned(), file_path: "test".to_owned()};
let matches = search_case_sensitive(&config, &contents);
print_matches(&matches);
Ok(config)
}
type Match<'a> = Vec<(&'a str, &'a str, &'a str)>;
// this function causes the compile error
// contents -> the full text content of the file i previously read
// config.query -> the string i'm searching for inside contents
fn search_case_insensitive<'a>(config: &Config, contents: &'a str) -> Match<'a> {
let mut matches: Match<'a> = Vec::new();
let query = config.query.to_lowercase();
for line in contents.lines() {
let indexes = line.to_lowercase().match_indices(&query);
for (i, found) in indexes {
let prev = &line[..i];
let next = &line[i + found.len()..];
matches.push((prev, found, next));
}
}
matches
}
and i get this error when compiling:
error[E0716]: temporary value dropped while borrowed
--> src/lib.rs:34:23
|
34 | let indexes = line.to_lowercase().match_indices(&query);
| ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
35 | for (i, found) in indexes {
| ------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
from what I understand, line
should be bounded to the 'a
lifetime because it is a reference to every element of contents
, but this is not happening, so line
is not borrowed but moved and i does not live long enough to be used in indexes
.
How can I instead borrow line from contents and bound it to the 'a
lifetime?
The problem is that line.to_lowercase()
is local to the function, but you're returning it.
The easiest fix is to make an owned String
:
type Match<'a> = Vec<(&'a str, String, &'a str)>;
// contents -> the full text content of the file i previously read
// config.query -> the string i'm searching for inside contents
fn search_case_insensitive<'a>(config: &Config, contents: &'a str) -> Match<'a> {
let mut matches: Match<'a> = Vec::new();
let query = config.query.to_lowercase();
for line in contents.lines() {
let line_lowercased = line.to_lowercase();
let indexes = line_lowercased.match_indices(&query);
for (i, found) in indexes {
let prev = &line[..i];
let next = &line[i + found.len()..];
matches.push((prev, found.to_owned(), next));
}
}
matches
}
However, note that your code is still incorrect. Specifically, to_lowercase()
can change character indices, and even in general lowercasing two strings and comparing them is not enough for a case-insensitive comparison with respect to Unicode.