How can I make my Rust function more versatile and efficient?

I have a function that works, but is more specialized than I would like, and has some disadvantages, I would like to fix it.

A working but incorrect function:

fn iter_to_min<T>(i:T) -> i64 where T:Iterator<Item=String>{ i.collect::<Vec<String>>() .iter() .flat_map(|s|s.split_whitespace()) .map(str::trim) .map(str::parse::<i64>) .map(Result::unwrap) .min() .expect("No min found.") } 

The reasons I don't like this implementation are as follows:

  • i64 hardcoded and I would like to reuse this function for u64 and possibly other return types
  • it collects this input only for immediate iteration, which is inefficient (heap allocation for no reason)
  • the closure passed to flat_map cannot be optimized by LLVM in all cases

and the closest I could get to my ideal function:

 use std::str::FromStr; fn iter_to_min<T,U>(i:T) -> U where T:Iterator<Item=String>,U: Ord+FromStr{ i.flat_map(str::split_whitespace) .map(str::trim) .map(str::parse::<U>) .map(Result::unwrap) .min() .expect("No min found.") } 

I see the following problems:

  • the argument passed to str::split_whitespace is String and will not force str
  • the argument passed to str::split_whitespace is not known to live long enough
  • Result::unwrap does not complain that the attribute core::fmt::Debug not implemented for the type <U as core::str::FromStr>::Err

I think that with smart life spans and Treit requirements, at least two of them can be fixed, and who knows, maybe there is a way to go three by three.

Sample code using some suggested fixes:

 use std::io::BufRead; use std::str::FromStr; use std::fmt::Debug; fn iter_to_min<T,U>(i:T) -> U where T:Iterator<Item=String>,U: Ord+FromStr, U::Err: Debug{ i.collect::<Vec<String>>() .iter() .flat_map(|s|s.split_whitespace()) .map(str::trim) .map(str::parse::<U>) .map(Result::unwrap) .min() .expect("No min found.") } fn main() { let a: Vec<_> = std::env::args().skip(1).collect(); let m:i64 = if a.is_empty() { let s = std::io::stdin(); let m = iter_to_min(s.lock().lines().map(Result::unwrap)); m }else{ iter_to_min(a.into_iter()) }; println!("{}", m); } 
+8
performance generics idioms rust
source share
2 answers

Unfortunately, you cannot do what you want, keeping both the general and the lack of distribution. The reason is because you need someone who owns your string data, but if flat_map(str::split_whitespace) is executed on the iterator of its lines, then there is no one to save these owned lines more, because str::split_whitespace only takes the line on which it is called, However, if you β€œpress” the ownership of the call chain, you cannot be completely generic and accept the lines belonging to them by value.

Therefore, there are two solutions: either cast the entire iterator to Vec<String> (or convert the elements provided by split_whitespace() to their own lines and put them together again in Vec ) or accept the link iterator.

Here is the most general version that I could find in the first solution:

 use std::str::FromStr; use std::fmt::Debug; fn iter_to_min<S, T, U>(i: T) -> U where S: Into<String>, T: IntoIterator<Item=S>, U: Ord + FromStr, U::Err: Debug { i.into_iter() .map(Into::into) .collect::<Vec<_>>() .iter() .flat_map(|s| s.split_whitespace()) .map(str::parse::<U>) .map(Result::unwrap) .min() .expect("No min found") } 

(try here )

This is basically the same as your first, but more general. Also note that you do not need to trim parts of the line after split_whitespace() - the latter will make sure that the parts of the line do not have spaces on the sides. Into<String> allows you to skip the iterators &str and String , and in the latter case, additional copies will not be executed.

Alternatively, you can split each line into its own lines separately:

 fn iter_to_min<S, T, U>(i: T) -> U where S: AsRef<str>, T: IntoIterator<Item=S>, U: Ord + FromStr, U::Err: Debug { i.into_iter() .flat_map(|s| s.as_ref().split_whitespace().map(String::from).collect::<Vec<_>>()) .map(|s| s.parse::<U>()) .map(Result::unwrap) .min() .expect("No min found") } 

Here we need to get only &str from the elements of the iterator, not String s, so I used AsRef<str> . However, each string must not only be converted to a sequence of String s; this sequence must be assembled into a vector for the same reason that was mentioned above - otherwise there would be no one to save the initial values ​​of type S from destruction.

But you can avoid .map(String::from).collect::<Vec<_>>() if you want to lose some pedigree. This is the second solution that I mentioned above. We can take an iterator for links:

 fn iter_to_min<'a, S: ?Sized, T, U>(i: T) -> U where S: AsRef<str> + 'a, T: IntoIterator<Item=&'a S>, U: Ord + FromStr, U::Err: Debug { i.into_iter() .map(AsRef::as_ref) .flat_map(str::split_whitespace) .map(|s| s.parse::<U>()) .map(Result::unwrap) .min() .expect("No min found") } 

(try here )

Roughly speaking, now the values ​​of S belong to someone else, and their lifetime is longer than the scope of iter_to_min() , so you do not need to convert each part to String , or collect the entire result of splitting a Vec<String> . However, you cannot pass Vec<String> this function; you can pass vec.iter() , however:

 let v: Vec<String> = vec!["0".into(), "1".into()]; iter_to_min(v.iter()) 

In all of these examples, I changed Iterator to IntoIterator - this is almost always what you want to use, not just Iterator . It allows, for example, to pass collections to such functions directly. Secondly, I added the U::Err: Debug condition, which is necessary for Result::unwrap to work. And finally, to fix the problem with "A string that does not force & str", you can always use explicit closures and the syntax of a method that would do this enforcement for you.

+8
source share

Solution without additional distributions

 use std::str::FromStr; fn iter_to_min<T, U>(i: T) -> Option<U> where T: Iterator<Item = String>, U: Ord + FromStr { i.filter_map(|string| { string.split_whitespace() .map(str::trim) .map(str::parse::<U>) .filter_map(Result::ok) .min() }) .min() } 
+3
source share

All Articles