Tumbleweed53
Tumbleweed53

Reputation: 1551

Understanding how to satisfy borrow checker when calling method on iterator twice?

In the below code, I understand that a borrow has been done by the call of zipped.filter. What I don't understand is how to fix it, if I want to use zipped again later.

I'm new to Rust, so if there are other problems or strange misuse of idioms in this code, I'm interested in that as well, but primarily about how to make the borrow work twice here.

Code:

fn main() {
    let data : Vec<String> = vec!["abc".to_string(), "def".to_string(), "bbc".to_string()];

    for s1 in &data {
        for s2 in &data {
            let zipped = s2.chars().zip(s1.chars());

            // Did a diff of the two strings have only one character different?
            if zipped.filter(|(a,b)| a != b).count() == 1 {
                let newStr = zipped.filter(|(a,b)| a == b).map(|(a,_)| a).collect::<String>();

                println!("String without the different character: {}", newStr);
            }
        }
    }
}

Error:

error[E0382]: use of moved value: `zipped`
   --> a.rs:10:30
    |
6   |             let zipped = s2.chars().zip(s1.chars());
    |                 ------ move occurs because `zipped` has type `Zip<Chars<'_>, Chars<'_>>`, which does not implement the `Copy` trait
...
9   |             if zipped.filter(|(a,b)| a != b).count() == 1 {
    |                       ---------------------- `zipped` moved due to this method call
10  |                 let newStr = zipped.filter(|(a,b)| a == b).map(|(a,_)| a).collect::<String>();
    |                              ^^^^^^ value used here after move
    |
note: this function consumes the receiver `self` by taking ownership of it, which moves `zipped`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0382`.

Upvotes: 2

Views: 256

Answers (1)

Alexey S. Larionov
Alexey S. Larionov

Reputation: 7927

You can always zipped.clone(), which clones the iterator so that you move the clone when you first call to filter(...).count(). The original zipped remains untouched and you'll move it by the second filter(...).collect() .

Note that it doesn't clone any data because iterators are lazy, copying an iterator means to copy its logic (so it copies logic of .chars(), .zip() etc which is just a bunch of function pointers, not the data).

fn main() {
    let data : Vec<String> = vec!["abc".to_string(), "def".to_string(), "bbc".to_string()];

    for s1 in &data {
        for s2 in &data {
            let zipped = s2.chars().zip(s1.chars());
            
            // << THE CHANGE IS HERE
            if zipped.clone().filter(|(a,b)| a != b).count() == 1 {
                let newStr = zipped.filter(|(a,b)| a == b).map(|(a,_)| a).collect::<String>();

                println!("String without the different character: {}", newStr);
            }
        }
    }
}

Output:

String without the different character: bc
String without the different character: bc

Upvotes: 2

Related Questions