2

I am learning Rust by following the Rust Book and I am currently trying to modify the project in Chapter 12, but I can't understand why my code is not working.

The function in question is the search function

fn search(query: &str, contents: String) -> Vec<String> { contents.lines().filter(|line| line.contains(query)).collect() } 

which is supposed to get contents of a file as a string and return a collection of the lines in the file containing query. In this form, it throws the error "a value of type std::vec::Vec<std::string::String> cannot be built from an iterator over elements of type &str".

I think that the error comes from the use of lines since it doesn't take ownership of contents. My question is if there is a better way to do this or if there is a similar method to lines that does take ownership.

4
  • 2
    Add .map (String::from) before the .collect(): playground Commented Sep 9, 2022 at 11:53
  • If you actually want to return an iterator owning content, you can also use the techniques described in Is there an owned version of String::chars? and How can I store a Chars iterator in the same struct as the String it is iterating on? Commented Sep 9, 2022 at 12:06
  • "My question is if there is a better way to do this or if there is a similar method to lines that does take ownership." there is not because it doesn't really make sense: usually the point of taking ownership is to be more efficient e.g. by reusing an existing allocation. But with an unspecified allocator you can't generically "split" a big allocation into lots of small ones, so there is no way for Rust to generically reuse contents' buffer for the lines. Therefore there's no point to an owning iterator, you can just map to a str->String conversion as Jmb shows. Commented Sep 9, 2022 at 13:17
  • "Therefore there's no point to an owning iterator" (except for borrinwing-related issues) Commented Sep 9, 2022 at 13:19

1 Answer 1

4

As a Rust learner it is important to know the differences between strings (String) and string slices (&str), and how those two types interact.

The lines() method of an &str returns the lines as iterator over &str, which is the same type. However the lines method of a string also returns an iterator over &str, which is the same type as before but in this case not the same type as the input. This means, your output will be of type Vec<&str>. However in that case you need a lifetime because otherwise you can't return a reference. In this case your example would look like this:

fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { contents.lines().filter(|line| line.contains(query)).collect() } fn main() { println!("found: {:?}",search("foo", "the foot\nof the\nfool")); } 

However if you want the vector to contain strings, you can use the to_owned() function to convert a &str into a String:

fn search(query: &str, contents: &str) -> Vec<String> { contents.lines().map(|line| line.to_owned()).filter(|line| line.contains(query)).collect() } fn main() { println!("{:?}",search("foo", "the foot\nof the\nfool")); } 

However this is inefficient because some strings are created that aren't used so it is better to map last:

fn search(query: &str, contents: &str) -> Vec<String> { contents.lines().filter(|line| line.contains(query)).map(|line| line.to_owned()).collect() } fn main() { println!("{:?}",search("foo", "the foot\nof the\nfool")); } 

Or with contents of type String, but I think this doesn't make much sense:

fn search(query: &str, contents: String) -> Vec<String> { contents.lines().map(|line| line.to_owned()).filter(|line| line.contains(query)).collect() } fn main() { println!("{:?}",search("foo", "the foot\nof the\nfool".to_owned())); } 

Explanation: Passing contents as a String isn't very useful because the search function will own it, but it is not mutable, so you can't change it to the search result, and also your search result is a vector, and you can't transform a single owned String into multiple owned ones.

P.S.: I'm also relatively new to Rust, so feel free to comment or edit my post if I missed something.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.