Rust shortest way to find the maximum product of a fixed length substring I'm trying to code in a Rust a function, that gives the maximum product of adjacent digits.
So given the string 123456789, with 3 adjacent digits, the maximum is 7 * 8 * 9 = 504.
I've tried to come up with this current functional solution (see previous edit for longer solutions). Could it be shortened? Comments are welcome.
The log is here, and desired, so the code runs quickly.
use std::iter::Map;
use std::str::Chars;

fn iter_log_digits(str: &amp;str) -&gt; Map&lt;Chars&lt;'_&gt;, fn(char) -&gt; f64&gt; {
  str.chars().map(|c: char| (c.to_digit(10).unwrap() as f64).ln())
}

/// Returns the maximum log sum of adjacent digit characters.
/// # Arguments
/// - `str`: Assumes there are no 0s.
/// - `adj_count`: The number of adjacent digit characters.
fn max_adj_log_sum_no_zeros(str: &amp;str, adj_count: usize) -&gt; f64 {
  if str.len() &lt; adj_count {
    return 0.;
  }
  let log_first: f64 = iter_log_digits(&amp;str[..adj_count]).sum();
  iter_log_digits(&amp;str).zip(iter_log_digits(&amp;str[adj_count..])).fold(
    (log_first, log_first), // `(log_cur, log_max)`
    |(log_cur, log_max), (x_left, x_right)| {
      let log_next = log_cur - x_left + x_right;
      (log_next, log_max.max(log_next))
    }
  ).1
}

fn max_product_no_zeros(str: &amp;str, adj_count: usize) -&gt; u64 {
  max_adj_log_sum_no_zeros(str, adj_count).exp().round() as u64
}

max_product_no_zeros(&quot;123456789&quot;, 3) should return 504.
General observations:

An indentation of two spaces is quite small.  The standard formatting uses four spaces.

Names like Map and Chars read better with their modules: iter::Map, str::Chars.

str: &amp;str — don't do that.

When str is not entirely made of decimal digits, your function panics by unwrapping.  Reporting an error via Result is generally preferred.  Magic also happens when str contains zeros.

Using fold here makes the semantics harder to understand.  A simple for loop might be more suitable.



Now, my intuition tells me floating point calculations are less efficient than just doing the operations on integers, so I wrote a benchmark.  Here's the result that I got:
running 4 tests
test l_f::tests::test ... ignored
test simonzack::tests::test ... ignored
test l_f::tests::bench       ... bench:  10,189,230 ns/iter (+/- 278,958)
test simonzack::tests::bench ... bench:  16,865,990 ns/iter (+/- 440,350)

test result: ok. 0 passed; 0 failed; 2 ignored; 2 measured; 0 filtered out

Using integers resulted in a ~1.7x speedup.  Do note that this benchmark is very crude, and you might want to measure your own use cases.

For reference, here's the code for the benchmark.  (Error checking is omitted for simplicity.)
#![feature(test)]

extern crate test;

mod l_f {
    fn to_digits(digits: &amp;str) -&gt; impl Iterator&lt;Item = u64&gt; + '_ {
        digits.chars().map(|c| c.to_digit(10).unwrap().into())
    }

    pub fn max_product_no_zeros(digits: &amp;str, length: usize) -&gt; u64 {
        if digits.len() &lt; length {
            return 0;
        }

        let mut current_product: u64 = to_digits(&amp;digits[..length]).product();
        let mut max_product = current_product;

        for (left, right) in to_digits(digits).zip(to_digits(&amp;digits[length..])) {
            current_product = current_product / left * right;
            max_product = max_product.max(current_product);
        }

        max_product
    }

    #[cfg(test)]
    mod tests {
        use test::Bencher;

        #[test]
        fn test() {
            assert_eq!(super::max_product_no_zeros(&quot;31415926&quot;, 3), 108);
        }

        #[bench]
        fn bench(b: &amp;mut Bencher) {
            let digits = test::black_box(&quot;9&quot;.repeat(1_000_000));

            b.iter(|| super::max_product_no_zeros(&amp;digits, 10));
        }
    }
}

mod simonzack {
    use std::iter::Map;
    use std::str::Chars;

    fn iter_log_digits(str: &amp;str) -&gt; Map&lt;Chars&lt;'_&gt;, fn(char) -&gt; f64&gt; {
        str.chars()
            .map(|c: char| (c.to_digit(10).unwrap() as f64).ln())
    }

    /// Returns the maximum log sum of adjacent digit characters.
    /// # Arguments
    /// - `str`: Assumes there are no 0s.
    /// - `adj_count`: The number of adjacent digit characters.
    fn max_adj_log_sum_no_zeros(str: &amp;str, adj_count: usize) -&gt; f64 {
        if str.len() &lt; adj_count {
            return 0.;
        }
        let log_first: f64 = iter_log_digits(&amp;str[..adj_count]).sum();
        iter_log_digits(&amp;str)
            .zip(iter_log_digits(&amp;str[adj_count..]))
            .fold(
                (log_first, log_first), // `(log_cur, log_max)`
                |(log_cur, log_max), (x_left, x_right)| {
                    let log_next = log_cur - x_left + x_right;
                    (log_next, log_max.max(log_next))
                },
            )
            .1
    }

    pub fn max_product_no_zeros(str: &amp;str, adj_count: usize) -&gt; u64 {
        max_adj_log_sum_no_zeros(str, adj_count).exp().round() as u64
    }

    #[cfg(test)]
    mod tests {
        use test::Bencher;

        #[test]
        fn test() {
            assert_eq!(super::max_product_no_zeros(&quot;31415926&quot;, 3), 108);
        }

        #[bench]
        fn bench(b: &amp;mut Bencher) {
            let digits = test::black_box(&quot;9&quot;.repeat(1_000_000));

            b.iter(|| super::max_product_no_zeros(&amp;digits, 10));
        }
    }
}