Enumerate a vector of results


#1

I am trying to parse the buf to get the numbers out while accounting for parsing errors.
What is the best way of iterating over vector and matching.
I were able to compile the following solutions

    let buf = "32X42X52";
    let mut result: Vec<_> = buf.split('X').map(|n| n.parse::<u32>()).collect();

    // Version 1
    for (i, ref item) in result.iter().enumerate() {
        match **item {
            Ok(n) => { /* use i and n */ },
            Err(ref ParseIntError) => return Err("Bad string".to_owned()),
        }
    }

    // Version 2
    for (i, item) in result.iter().enumerate() {
        match item {
            &Ok(n) => { /* use i and n */ },
            &Err(ref ParseIntError) => return Err("Bad size in gift sizes".to_owned()),
        }
    }

And also is there a way of dropping Err variants before .collect() call in the chain?


#2

let mut result: Vec<_> = buf.split('X').map(|n| n.parse::<u32>()).collect();

Try to be more lazy, delay the collect(), or avoid it. In your code collect() is not necessary.

fn main() {
    let buf = "32X4aX52";
    let result = buf
                 .split('X')
                 .filter_map(|n| n.parse::<u32>().ok());
    println!("{:?}", result.collect::<Vec<_>>());
}

Note: println!() not being able to print a lazy iterable is a hole in the std lib that needs to be fixed.


#3

Don’t iterators already implement Debug? I thought most types had it now…


#4

A lot of them were fixed in https://github.com/rust-lang/rust/pull/32054 (I think that’s going to be in Rust 1.9)

The result in the example has this output: (Which I guess some parts of can be improved down the line).

FilterMap {
    iter: Split(
        SplitInternal {
            start: 0,
            end: 8,
            matcher: CharSearcher(
                CharEqSearcher {
                    char_eq: 'X',
                    haystack: "32X4aX52",
                    char_indices: CharIndices {
                        front_offset: 0,
                        iter: Chars {
                            iter: Iter(
                                [
                                    51,
                                    50,
                                    88,
                                    52,
                                    97,
                                    88,
                                    53,
                                    50
                                ]
                            )
                        }
                    },
                    ascii_only: true
                }
            ),
            allow_trailing_empty: true,
            finished: false
        }
    )
}

#5

I’ve just seen that this compiles:

fn main() {
    let buf = "32X4aX52";
    let result = buf
                 .split('X')
                 .filter_map(|n| n.parse::<u32>().ok());
    println!("{:?}", result);
}

And it prints:

FilterMap { iter: Split(SplitInternal { start: 0, end: 8, matcher: CharSearcher(CharEqSearcher { char_eq: ‘X’, haystack: “32X4aX52”, char_indices: CharIndices { front_offset: 0, iter: Chars { iter: Iter([51, 50, 88, 52, 97, 88, 53, 50]) } }, ascii_only: true }), allow_trailing_empty: true, finished: false }) }

But this is really different from what I want. I want an output similar to:

[32; 52]


#6

It is debugging output though. I expect that some of the iterators will have their debug output made much prettier, for example the Chars iterator should show the string of course and so on.

I don’t expect you will ever see the output you desire, because there is a closure the debugging code can’t call inbetween (it doesn’t know how sideeffectful it might be). But the parts that have no side effects, like the string splitting, can all be computed in Debug.


#7

This is almost terrible :frowning:

I hate the need to write .collect::<Vec<_>>() in my code, I’ve written that already hundreds of times :frowning:

Then can’t we add a special printing function/macro in the Rust Prelude that performs the collect+printing?


#8

I’m a fan of making the tools I need to express myself. Why not make a function?

(playground link)

fn vec<I>(iter: I) -> Vec<I::Item> where I: IntoIterator {
    iter.into_iter().collect()
}

// ... println!("{:?}", vec(result));

#9

It could be OK as long as the function is in the Prelude… But there’s another bigger problem. If I print 20 MB of text of an iterable, I don’t want to allocate in memory the whole iterable. I’d like to print it on a file in a lazy way (this is what D language does with its ranges. You don’t need to convert them to arrays before printing them).


#10

By the way, I think use std::iter::FromIterator and Vec::from_iter(...) is a nicer more explicit way to do the same collect operation. Less punctuation to type, too.


#11

This is a slight different topic. I think the std library needs something like a “to_vec()” function that works with iterables, because converting iterables to vectors is a quite common operation in Rust.


#12

Thank you @leonardo

In terms of matching style (if not using filter_map), which one is considered better style?


#13

Now I’ve found that the itertools crate seems to have what I asked for the standard library (and the output generation is lazy):

extern crate itertools;
fn main() {
    use itertools::Itertools;
    println!("{}", (0 .. 1_000_000).format(", ", |elt, g| g(&elt)));
}

For simplicity, probably I prefer two functions, one of them requires just the separator string, and the other takes the closure too:

println!("{}", (0 .. 1_000_000).format(", "));
println!("{}", (0 .. 1_000_000).format_by(", ", |elt, g| g(&elt)));

#14

That’s a good suggestion. I’m happy you found .format(), I think it’s a pretty nifty tool.


#15

Thanks for the idea, there’s now a .format_default(separator) in itertools 0.4.13. The nice name format is already taken unfortunately.