1

I have this closure color, but the type system cannot infer the type of its pixel argument.

pub fn solve_part2(input: &[u32]) -> String {
    let color = |pixel| {
        pixel
            .skip_while(|l| **l == 2)
            .next()
            .map(|c| char::from_digit(*c, 10).unwrap())
            .unwrap()
    };

    let pixel = |n| input.iter().skip(n).step_by(25 * 6);
    (0..(25 * 6)).map(|px| color(pixel(px))).collect()
}

With the type annotated, it becomes

let color = |pixel: std::iter::StepBy<std::iter::Skip<std::slice::Iter<u32>>>| {
    pixel
        .skip_while(|l| **l == 2)
        .next()
        .map(|c| char::from_digit(*c, 10).unwrap())
        .unwrap()
};

Are there any tricks to help the type system infer the type of pixel without annotating?

Brady Dean
  • 3,378
  • 5
  • 24
  • 50
  • If the `color` closure is only used once, you could move it inline inside the `map` call. – Dai Jan 12 '20 at 19:46
  • @Dai That is how I had it initially, but moved it out for clarity. It did work fine inlined. – Brady Dean Jan 12 '20 at 19:48
  • Another option is to use a type-alias. You could also make it more succint by importing those types into-scope - Remember that Rust isn't C++ so you don't need to avoid importing namespaces (unlike `using namespace std;` in C++): https://stackoverflow.com/questions/29013617/rust-use-vs-c-using-namespace - so there's nothing wrong with having `StepBy>>` in your code. – Dai Jan 12 '20 at 19:53
  • Partially relevant discussion about why the type annotations are needed in closures: https://www.google.com/amp/s/amp.reddit.com/r/rust/comments/5g6oa0/can_i_hint_the_types_of_closure_parameters_in/ – sshashank124 Jan 12 '20 at 19:57
  • Could be a [known issue](https://github.com/rust-lang/rust/issues/12679). – edwardw Jan 12 '20 at 20:31

1 Answers1

4

So far, I could not come up with something without annotations, but you could reduce the amount of annotation:

You could leave some holes that are to be filled by type inference:

pub fn solve_part2(input: &[u32]) -> String {
    let color = |pixel: std::iter::StepBy<_>| {
        pixel
            .skip_while(|l: &&_| **l == 2)
            .next()
            .map(|c| char::from_digit(*c, 10).unwrap())
            .unwrap()
    };
    let pixel = |n| input.iter().skip(n).step_by(25 * 6);
    (0..(25 * 6)).map(|px| color(pixel(px))).collect()
}

Alternatively, inline color:

pub fn solve_part3(input: &[u32]) -> String {
    let pixel = |n| input.iter().skip(n).step_by(25 * 6);
    (0..(25 * 6)).map(|px| pixel(px)
            .skip_while(|l| **l == 2)
            .next()
            .map(|c| char::from_digit(*c, 10).unwrap())
            .unwrap()).collect()
}

Alternatively, make color a local fn:

pub fn solve_part3(input: &[u32]) -> String {
    fn color<'a>(pixel: impl Iterator<Item=&'a u32>) -> char {
        pixel
            .skip_while(|l| **l == 2)
            .next()
            .map(|c| char::from_digit(*c, 10).unwrap())
            .unwrap()
    };
    let pixel = |n| input.iter().skip(n).step_by(25 * 6);
    (0..(25 * 6)).map(|px| color(pixel(px))).collect()
}
phimuemue
  • 34,669
  • 9
  • 84
  • 115