Rust noob here. Is there a way to not move ownership from chopped.count()?
let chopped = decrypted.split("\r\n");
let size = chopped.count(); // ownership moved
...
for line in chopped { // ERROR: value used here after move
...
The Iterator::count method consumes the iterator, you can't change that. What you can do is call Iterator::collect to create a Vec from the iterator, then call Vec::len to get the count, and then iterate the Vec with your for loop.
Depending on the length of the input, this might be needlessly expensive (two linear scans of the input instead of one).
If speed is important or the input is large, go with a stupidly simple counter (increment it in the for loop).
This also avoids the many allocations necessitated by the collect-to-vec solution.
If you are worried about the expense of iterating twice, you can use inspect with a closure that increments a counter variable in the containing scope when you consume the iterator later. I would provide an example normally, but Iām on mobile.
Many iterators support cloning, and they get cloned themselves (their iteration state), without cloning the underlying data.
There's also iter.by_ref() that technically prevents giving ownership, but the state of iteration will be shared, and count() iterates until the end, so it will "use up" the iterator, and the next for loop will always see 0 items.
Yeah, that's what the compiler suggest me to do. I am trying to avoid clone() since I assumed it would consume extra memory and feel a bit unnecessary. Unless this is standard in Rust?
The clone is fine and cheap in this case because the underlying iterator (i.e. The struct that implements the Iterator trait returned by split(). This one to be precise) is only (mostly?) holding a reference to the str decrypted.
The work is done while iterating (Someone please correct me if I'm wrong here!) to find the next split each time you call next.
You would do all that work once to find the count, and have to do all that work again to iterate through each split and do your thing.
To avoid doing that work twice, you have the option of saving the splits to a Vec. Which involves the many allocations (Vec will reallocate as it grows larger, this can be mitigated by sizing the Vec up front if you happen to know the length - but since you don't, you can't).
Or you could also avoid doing all the work twice by counting as you do the thing, but then the count isn't available while doing the thing, since you'll only have the final count during the last iteration.
Whether to do the work twice or to allocate a Vec is your choice. I expect the Vec will use more memory, but I can't be sure which will be faster.
Cloning the Iterator itself though should have more or less negligible impact.
That's correct; iterators are lazy (or poorly implemented).
Edit: What I meant is that iterators which are not lazy are generally considered to be poorly implemented (though there are exceptions where being lazy isn't possible or practical).
Why are iterators that do all their work upfront generally considered poor implementations? Expectations (around behavior / allocation avoidance / latency, etc). There are exceptions where it's unavoidable, but split isn't one of them.