this post was submitted on 10 Apr 2024
32 points (100.0% liked)
Rust
5651 readers
27 users here now
Welcome to the Rust community! This is a place to discuss about the Rust programming language.
Wormhole
Credits
- The icon is a modified version of the official rust logo (changing the colors to a gradient and black background)
founded 1 year ago
MODERATORS
you are viewing a single comment's thread
view the rest of the comments
view the rest of the comments
I don't know and don't think so, but what you are doing is better done with retain anyways.
I mean, the actual operation is just an example, of course. Feel free to make it a
.map()
operation instead. The strings couldn’t be reused then, but the vector’s allocation still could… in theory.map()
can still be used withVec::iter_mut()
,filter_map()
can be replaced withVec::retain_mut()
.Yeah, that's helpful if I would be currently optimizing a hot loop now. But I was really just using it as an example. Also,
retain_mut()
doesn't compose as well.I'd much rather write:
Over:
And it would be nice if that would be optimized the same. After all, the point of Rust's iterators is to provide zero-cost abstractions. In my opinion, functions like
retain_mut()
represent a leakiness to that abstraction, because the alternative turns out to not be zero cost.Is it really fair to say retain doesn't compose as well just because it requires reference-based update instead of move-based? I also think using move semantics for in-place updates makes it harder to optimise things like a single field being updated on a large struct.
It also seems harsh to say iterators aren't a zero-cost abstraction if they miss an optimisation that falls outside what the API promises. It's natural to expect
collect
to allocate, no?But I'm only writing this because I wonder if I haven't understood your point fully.
(Side note: I think you could implement the API you want on top of
retain_mut
by usingstd::mem::replace
with a default value, but you'd be hoping that the compiler optimises away all thereplace
calls when it inlines and sees the code can't panic. Idk if that would actually work.)The composability doesn't have much to do with whether it's a reference or a move, it's because it bypasses usage of the
Iterator
methods. Iterators chains can consist offilter
,map
and other operations, which allows various functions and/or closures to be composed together. Whereas withretain_mut()
there isn't really a chain and functions you may otherwise use in an iterator chain become harder to (re)use.You're right, I wouldn't say iterators aren't a zero-cost abstraction. But most abstractions are also leaky -- it's just the extent in which the leakiness is exposed that makes them more or less effective. As such, saying to just use
retain_mut
instead of the iterator approach highlights the shortcoming of the abstraction. But if the same results could be achieved while still using the same iterator, that would make that abstraction more useful and consistent. And that's great, because that means we need to worry less when using iterators :)https://blog.polybdenum.com/2024/01/17/identifying-the-collect-vec-memory-leak-footgun.html might be relevant to your question.
along with the related https://github.com/rust-lang/rust/issues/120091
Thanks! That’s very much what I was looking for!
To be fair, these alternatives are also limited to the case where the item type stays the same.