How to pass a modified string parameter?
I'm in chapter 12 of the Rust programming language, which implements a case-insensitive line search. It doesn't make sense to me to implement the same logic twice, so I figured out if I just call the case-sensitive search function with a case-sensitive argument, that might work. It doesn't.
Here is my invalid code:
fn main() {
let a = search("Waldo", "where in\nthe world\nis Waldo?");
let b = search("waldo", "where in\nthe world\nis Waldo?");
let c = search_case_insensitive("waldo", "where in\nthe world\nis Waldo?");
println!("{:?}", a);
println!("{:?}", b);
println!("{:?}", c);
}
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let mut results = Vec::new();
for line in contents.lines() {
if line.contains(query) {
results.push(line);
}
}
results
}
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let query = query.to_lowercase();
let contents2: &str = &contents.to_lowercase();
search(&query, contents2)
}
The errors in most of the versions I've come up with are inevitably very similar:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:25:28
|
25 | let contents2: &str = &contents.to_lowercase();
| ^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
...
28 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 23:1...
--> src/main.rs:23:1
|
23 | pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit 2:
Now that you've updated your question with MCVE, and have stated that you're not worried about deviating from the book's example... Here's another version that relies on the extra assignment by using String
:
fn main() {
let a = search("Waldo", "where in\nthe world\nis Waldo?");
let b = search("waldo", "where in\nthe world\nis Waldo?");
let c = search_case_insensitive("waldo", "where in\nthe world\nis Waldo?");
println!("{:?}", a);
println!("{:?}", b);
println!("{:?}", c);
}
pub fn search<S>(query: S, contents: S) -> Vec<String> where S: Into<String> {
let query = query.into();
let mut results = Vec::new();
for line in contents.into().lines() {
if line.contains(&query) {
results.push(line.into());
}
}
results
}
pub fn search_case_insensitive<S>(query: S, contents: S) -> Vec<String> where S: Into<String> {
let query = query.into().to_lowercase();
let contents = contents.into().to_lowercase();
search(query, contents)
}
edit:
I realize I never really gave you other options. Here's what I might do:
pub enum SearchOptions {
CaseSensitive,
CaseInsensitive
}
pub fn search<'a>(query: &str, contents: &'a str, options: SearchOptions) -> Vec<&'a str> {
let mut results = Vec::new();
for line in contents.lines() {
let check = match options {
SearchOptions::CaseSensitive => line.contains(query),
SearchOptions::CaseInsensitive => line.to_lowercase().contains(&query.to_lowercase()),
};
if check {
results.push(line);
}
}
results
}
This is about "deduplication".
Original answer:
The actual problem is that you are trying to pass it contents
when it is tied to the lifecycle 'a
. However, what you really want to be "case insensitive" is query
.
This is not bound to lifetime in 'a
exactly the same way , so... works:
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let query = query.to_lowercase();
search(&query, contents)
}
However, you still need to repeat the logic...because you need to match lowercase queries to lowercase rows...this is demonstrated in the book's example:
if line.to_lowercase().contains(&query) {
// ^^^^^^^^^^^^^^ each LINE is converted to lowercase here in the insensitive search
results.push(line);
}
"How do I stop repeating logic?" - well, they weren't quite the same in the first place. I don't think your attempt is quite the same as the original one (nice to be corrected though).