Live data from Hacker News

The humble for loop in Rust

blog.startifact.com

11–20 of 60 posts

Re: The humble for loop in Rust

#11
The author's fold example is unfair. They could've just called accumulator.extend() and then return the accumulator inside the fold example for a fair apple to apple comparison. Just mark the accumulator as mut.

Furthermore, I'd use with_capacity in both cases: Vec::with_capacity(list_of_lists.iter().map(|l| l.len()).sum())

Re: The humble for loop in Rust

#12
post #9
post #6

Earlier quoted context omitted.

Ah thanks! I was going to ask in here to see if anyone knew because the explanation about overwriting the same vector seemed pretty off base. Never worked in Rust though so wondered if the iterator api had some weird optional notion of size that could be utilised throughout the chain.

> Never worked in Rust though so wondered if the iterator api had some weird optional notion of size that could be utilised throughout the chain. Fwiw, this does exist: [Iterator::size_hint] ( https://doc.rust-lang.org/std/iter/trait.Iterator.html#metho... )

Yep that can be used for pre allocating the Vec like in the `with_capacity` example

Re: The humble for loop in Rust

#13
post #9

Earlier quoted context omitted.

> Never worked in Rust though so wondered if the iterator api had some weird optional notion of size that could be utilised throughout the chain. Fwiw, this does exist: [Iterator::size_hint] ( https://doc.rust-lang.org/std/iter/trait.Iterator.html#metho... )

Yep that can be used for pre allocating the Vec like in the `with_capacity` example

That's not accurate, it can be used while consuming an Iterator and, depending on the implementation, be used to guide the consumer during runtime. The stdlib likely is not doing this but the API very much allows advanced behavior. We, e. G., used this for some part of a query engine in a course in uni to guide algorithm choice for operators.

Re: The humble for loop in Rust

#14
post #2

Anyone have examples where fold leads to easier to read code than a for loop in Rust?

I have used fold for converting strings to a bitset in advent of code let string = "ewfsan"; let bitset = string.bytes().fold(0u32 |acc, ch| acc | 1 This is a idiom that I have used many times so this being more consice than a for loop is a plus Of course if you have never seen a syntax before it will make less sense that anything you have seen before

afair I've mostly only used fold when doing maths not covered by the standard sum or product. Fold is similar to map reduce but it's just one expression.

Re: The humble for loop in Rust

#15
post #2

Anyone have examples where fold leads to easier to read code than a for loop in Rust?

Readability is subjective. I personally find fold almost always more readable than a for loop when the accumulator variable has a simple type. This is because merely seeing fold can already telling me several things: it will iterate over the entire collection without early exits like "break" in a loop; the data dependency between each iteration is made clear into a single variable.

I find it slightly difficult to read when the accumulator variable actually has multiple parts, like a complicated tuple. It's worse when part of the accumulator is a bool indicating whether it's finished; that's just a poor emulation of "break" in a for loop.

Re: The humble for loop in Rust

#16
post #9

Earlier quoted context omitted.

> Never worked in Rust though so wondered if the iterator api had some weird optional notion of size that could be utilised throughout the chain. Fwiw, this does exist: [Iterator::size_hint] ( https://doc.rust-lang.org/std/iter/trait.Iterator.html#metho... )

It seems `map` should have less restrictive semantics (specifically ordering) than `for`, does that allow more optimization? I don't know much about Rust internals.

Reading the godbolt it looks like for the push loop llvm is unable to remove the `grow_one` capacity check after every push. Becaue of this the Vec could possibly reallocate after every push meaning it can't auto vectorize.

Re: The humble for loop in Rust

#17
Yeah I kind of wish there was a way to still use `?` inside map/filter/etc. lambdas. Error handling with that functional style is generally way more awkward than for loops, but also it's often more elegant in other ways (e.g. Rayon).

I think Ruby has some kind of feature that works like that but IIRC it looked less foot-gun more foot-bazooka. Does anyone know of any languages that solve that problem elegantly?

Re: The humble for loop in Rust

#18
Why does fallible_flatten_fold have that accumulator.clone() in it? You're cloning the in-progress vector only to throw away the original, it's extremely wasteful and completely unnecessary. Just declare the accumulator as `mut`.

Re: The humble for loop in Rust

#19
post #4

> Why is map so much faster? I am not sure. I suspect with the map() option the Rust compiler figures out it can avoid allocations altogether by simply writing over the original vector, while with the loop it can't. Or maybe it's using SIMD? I tried to look in the compiler explorer but I'm not competent enough yet to figure it out. Maybe someone else can explain! Yep, it's due to SIMD -- in the assembly for `using_ma…

SIMD is true, but the original guess is correct, and that effect is bigger!

using_map is faster because it's not allocating: it's re-using the input array. That is, it is operating on the input `v` value in place, equivalent to this:

    pub fn using_map(mut v: Vec) -> Vec {
        v.iter_mut().for_each(|c| *c += 1);
        v
    }
This is a particularly fancy optimization that Rust can perform.

Re: The humble for loop in Rust

#20
It’s interesting to note that performance of a for loop versus the functional-style mechanism varies by language. On Java, there is a performance penalty (possibly shrunken since Java 8) for using the FP idioms while in Rust, they end up much faster.
Post reply on HN