This question already has answers here:
How to get the index of the current element being processed in the iteration without a for loop?
(2 answers)
Closed 3 months ago.
So I'm following with the Rust Book tutorial on writing a grep clone with Rust. The book at first gives the example of this function to search a file for a given string:
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
}
Which I then modified to that results would include the line number the match was found on, like so:
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<String> {
let mut results = Vec::new();
for (index, line) in contents.lines().enumerate() {
if line.to_lowercase().contains(&query) {
let line_found = &index + 1;
results.push(String::from(format!("Line {line_found}: {line}")));
}
}
results
}
So then afterwards, the books shows how to use an iterator to make the code simpler and cleaner:
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
contents
.lines()
.filter(|line| line.contains(query))
.collect()
}
And I'm struggling to figure out how I would obtain the same functionality to include the line number the match was found on with this function. In collect() is there a way for me access the index of the iterator and the line itself?
Use enumerate, which transforms an Iterator<Item = T> to an Iterator<Item = (usize, T)> where the first element of the tuple is your index. You already used it in your second example, it can be used in the transformed version as well since it's still an iterator combinator.
Related
This question already has answers here:
How to convert an Iterator on a tuple of (String, String) to an Iterator of (&str, &str)?
(2 answers)
Closed 2 months ago.
As the title says, how would one go about converting an Iterator<Item = T> to Iterator<Item = &'a T> (which is to be passed to a function) without collecting the contents of the iterator?
Basically, I want to accomplish the following:
fn foo<'a, T>(data: impl Iterator<Item = &'a T>){
// ..
}
let data: Vec<T> = ...;
let iter = data.iter().map(|t| {
let mut nt = t.clone();
// mutate nt...
return nt;
});
foo(iter); // Error: expected &T, iter returns T
The only way I can think of to get references to items without an owner is to leak them. It's most likely not what you want but here it is anyways:
fn leak_it<T>(i: impl Iterator<Item = T>) -> impl Iterator<Item = &'static T> {
i.map(|x| &*Box::leak(Box::new(x)))
}
To avoid leaking the memory for good either you have to store the references (in which case you should just collect the items in the first place) or the function you're calling has to return them so you can turn them back into a box with Box::from_raw and properly free the memory.
Suppose I have a Vec<&str> (I have no control over this type) and I need to transform each string. Since the elements are string slices (which don't own the string), I need to store the actual strings somewhere.
I thought about storing the new strings in a container that's able to grow without reallocating. So I came up with the following:
use std::collections::VecDeque;
fn transform(s: &str) -> String {
s.to_owned() + "blah"
}
pub fn func<'a>(strings: &mut Vec<&'a str>, string_storage: &'a mut VecDeque<String>) {
for string in strings.iter_mut() {
string_storage.push_back(transform(string));
*string = string_storage.back().unwrap().as_str();
}
}
Of course, it doesn't work (I get error[E0502]: cannot borrow `*string_storage` as mutable because it is also borrowed as immutable). I understand why the error happens, but I've struggled for some time to figure out a solution.
The best I could come up with is to separate the pushes and assignments into two loops:
pub fn func<'a>(strings: &mut Vec<&'a str>, string_storage: &'a mut Vec<String>) {
for string in strings.iter() {
string_storage.push(transform(string));
}
for (i, string) in strings.iter_mut().enumerate() {
*string = string_storage[i].as_str();
}
}
But it seems weird to have to iterate twice. Is there a simpler solution?
I'm trying to implement a L-System struct and am struggling with it. I already tried different approaches but my main struggle comes from lifetime of references. What I'm trying to achieve is passing the value of the applied axioms back to my system variable, which i passed with the necessary lifetime in apply_axioms_once.
use std::collections::HashMap;
struct LSytem<'a> {
axioms: HashMap<&'a char, &'a str>,
}
impl<'a> LSytem<'a> {
fn apply_axioms_once(&mut self, system: &'a mut str) -> &'a str {
let mut applied: String = String::new();
for c in system.chars() {
let axiom = self.axioms.get(&c).unwrap();
for s in axiom.chars() {
applied.push(s);
}
}
system = applied.as_str();
system
}
fn apply_axioms(&mut self, system: &'a str, iterations: u8) -> &'a str {
let mut applied: &str = system;
// check for 0?
for _ in 0..iterations {
applied = self.apply_axioms_once(applied);
}
&applied
}
}
I already read a couple of similar questions, but still can't quite wrap my head around it. What seems to be the most on point answer is https://stackoverflow.com/a/42506211/18422275, but I'm still puzzled about how to apply this to my issue.
I am still a beginner in rust, and way more bloody than i thought.
This can't work because you return a reference of a data created inside the function (so the given data has a lifetime until the end of the function scope, the returned reference would point to nothing).
You shoud try to return String from your functions instead, so the returned data can be owned.
I made this example to try out:
use std::collections::HashMap;
struct LSytem<'a> {
axioms: HashMap<&'a char, &'a str>,
}
impl<'a> LSytem<'a> {
fn apply_axioms_once(&mut self, system: &String) -> String {
let mut applied: String = String::new();
for c in system.chars() {
let axiom = self.axioms.get(&c).unwrap();
for s in axiom.chars() {
applied.push(s);
}
}
applied
}
fn apply_axioms(&mut self, system: &String, iterations: u8) ->String{
let mut applied = String::from(system);
// check for 0?
for _ in 0..iterations {
applied = self.apply_axioms_once(system);
}
applied
}
}
fn main() {
let mut ls = LSytem {axioms: HashMap::new()};
ls.axioms.insert(&'a', "abc");
let s = String::from("a");
ls.apply_axioms(&s,1);
}
I'm working through the book and I'm not understanding why this function doesn't compile:
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
contents
.lines() // Fetch an iterator for each line in `contents`
.map(|x| x.to_lowercase()) // (x is now String) Convert each line to lowercase
.filter(|x| x.contains(query)) // Filter out lines that do not contain query
.map(|x| x.trim()) // Eliminate extra whitespace
.collect() // Consume iterator and produce Vec<&str>
}
Without the to_lowercase() line it will run, and I'm guessing that is because that will return a String instead of the &str we'll need to output at the end. However when I either substitute a conversion back to &str like:
// -- snip --
.map(|x| x.to_lowercase().to_str())
// -- snip --
This states that a temporary value is being referenced. Which I assume because &str reference the String, when the String is released it makes my &str invalid as well.
Are closures just not a good way of handling this, and I should break it into different statement?
This states that a temporary value is being referenced. Which I assume because &str reference the String, when the String is released it makes my &str invalid as well.
This assumption is correct.
Are closures just not a good way of handling this, and I should break it into different statement?
No amount of refactoring that function will change the fact that to_lowercase() requires modifying the &str and has to produce a String, so if lowercasing the contents is a requirement then this is the best you can do:
fn search(query: &str, contents: &str) -> Vec<String> {
contents
.lines() // Fetch an iterator for each line in contents
.map(|x| x.trim().to_lowercase()) // Trim & lowercase string
.filter(|x| x.contains(query)) // Filter out lines that do not contain query
.collect() // Consume iterator and produce Vec<String>
}
If you want to perform case-insensitive filtering but still return the unmodified contents (no lowercasing) then you can do this:
fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
contents
.lines() // Fetch an iterator for each line in contents
.filter(|x| x.to_lowercase().contains(query)) // Filter out lines that do not contain query
.map(|x| x.trim()) // Trim whitesapce
.collect() // Consume iterator and produce Vec<&'a str>
}
For the person that follows behind me, here's where I ended up. Thanks #pretzelhammer.
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let query = query.to_lowercase(); // transform query to lowercase()
contents
.lines()
.filter(|x| x.to_lowercase().contains(&query))
.map(|x| x.trim())
.collect()
}
When I try to compile this code:
use ignore::WalkBuilder;
fn main() {
let walk_iter = WalkBuilder::new("./").hidden(false).build();
let mut walk_paths: Vec<std::path::PathBuf> = Vec::new();
for entry in walk_iter {
let entry = entry.unwrap().into_path();
walk_paths.append(entry);
}
}
I get the error when appending the entry:
mismatches types, expected mutable reference &mut std::vec::Vec<std::path::PathBuf> found struct std::path::PathBuf
The std::vec::Vec::append method appends the content of another vector into the current one:
pub fn append(&mut self, other: &mut Vec<T>)
Moves all the elements of other into Self, leaving other empty.
You are looking for std::vec::Vec::push:
pub fn push(&mut self, value: T)
Appends an element to the back of a collection.