Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should .cloned() be before or after .filter()

Tags:

iterator

rust

Let's say that I have vector and I want just keep the even elements. I would need to used cloned() and filter(). For example:

fn main() {
    let my_vec: Vec<i32> = vec![1,2,3,4];

    let my_vec_1: Vec<i32> = my_vec.iter().cloned().filter(|&x| x % 2 == 0).collect();
    println!("{:?}", my_vec_1);

    let my_vec_2: Vec<i32> = my_vec.iter().filter(|&x| x % 2 == 0).cloned().collect();
    println!("{:?}", my_vec_2);

}

Both approaches work. Using cloned() after filter() seems a little bit more efficient. Because then I don't have to convert all elements of the iterator from &T to T, but only the ones that have been filtered. In my example that's half the elements.

However, I seem to see cloned() applied before filter(). Here is one example: method.inspect

I thought that maybe .cloned() has to be used before for types that don't implement Copy trait, but it does not seem to be the case: nested vec example. Also, because filter uses FnMut(&Self::Item), I don' think that should be a problem.

Are there advantages to using cloned() before filter()? Is this more of a stylistic issue? If so, is there preferred style?

like image 867
Akavall Avatar asked Dec 10 '22 18:12

Akavall


2 Answers

This is an alternative to Matthieu M.'s answer.

When you have small, Copy elements, you should Clone them as soon as possible. Cloning is nearly always free in these cases, but I have seen extra indirection confuse the optimizer. When you call iter on a container of integers, cloned should nearly always be the one to follow it unless you can merge it into the next call, eg. by an extra dereference in a call to map.

So in the case given, using cloned early is entirely sensible. It's true that this is a micro-optimization, but it's so easy to do and often makes the types simpler to work with, so I see no downsides to doing it.

In the future, copied will likely become available as the preferred way to handle this.


When working with non-Copy types, where Clone might not be so cheap, delaying cloning is a much better default.

like image 147
Veedrac Avatar answered Dec 13 '22 08:12

Veedrac


This is not a matter of style.

The example of inspect is made to show-case inspect, that is all. It uses .cloned in an arguably silly way, but cloned was probably chosen because of its easy to understand semantic so as to create an easy to understand "complex iterator sequence".


So, .cloned() before or after .filter(...) ? As you mention, unless cloning is necessary for filtering (which would be surprising) the rule of thumb will be to clone after so as to minimize the number of cloned elements.

No style here, just a pragmatic performance assessment.

like image 32
Matthieu M. Avatar answered Dec 13 '22 07:12

Matthieu M.