Viable alternative to .clone() here?

My code works. It's just that I don't want to use .clone() since I'm using it on a large vector in a loop of the length of that vector.
Here is the code:

fn part2(data: Vec<String>) -> i64 {
    let datalen: usize = data.len();
    for i in 0..datalen {
        let currentelem = &data[i];
        let twoparts = currentelem.split(" ").collect::<Vec<&str>>();
        let switch: &str;
        if twoparts[0] == "jmp" {
            switch = "nop";
        } else if twoparts[0] == "nop" {
            switch = "jmp"
        } else {
            continue;
        }
        let mut mdata = data.clone();
        mdata[i as usize] = format!("{} {}",switch, twoparts[1]);
        let val = part1(&mdata,&mut Vec::new(),0i64,0i64,true);
        if val != 0 {
            return val;
        }
    }
    // Not happening
    return 666i64;
}

Sample data for this:

[
"nop +355"
"acc +46"
"jmp +42"
]

As you see in let mut mdata = data.clone(); I am cloning the vector to create a modified version to use temporarily. It's just really inefficient to do this.
I'm thinking of making the data parameter mutable, changing the needed value and then reversing it to how it was before to use in the next iteration of the loop, but I don't know how to do this. What is the best course of action now and how to do it?

You can use std::mem::take to move the current element out of the vector. Then you can put the new element in its place before calling part1. Finally, you can put the original element back afterwards, so the vector is returned to its original state:

use std::mem::take;

fn part2(mut data: Vec<String>) -> i64 {
    let datalen: usize = data.len();
    for i in 0..datalen {
        let currentelem = take(&mut data[i]);
        let twoparts = currentelem.split(" ").collect::<Vec<&str>>();
        let switch: &str;
        if twoparts[0] == "jmp" {
            switch = "nop";
        } else if twoparts[0] == "nop" {
            switch = "jmp"
        } else {
            continue;
        }
        
        data[i] = format!("{} {}",switch, twoparts[1]);
        let val = part1(&data, &mut Vec::new(), 0i64, 0i64, true);
        data[i] = currentelem;
        
        if val != 0 {
            return val;
        }
    }
    // Not happening
    return 666i64;
}

(Playground)

3 Likes

This should work...
But it doesn't oddly enough.
It gives a different solution (output) then when I use my current code, (which I know is correct since the website said I had the correct solution).


I made two println!() statements and the code behaves exactly how it's supposed to behave, so I'm not sure what's going wrong here.
Here's the solution with your adjustments:

Here's the solution with my original code:

There's a bug in my code: If the loop hits the continue statement, then it fails to put the current item back into the vector. The simplest fix is to restore data[i] right before the continue:

        if twoparts[0] == "jmp" {
            switch = "nop";
        } else if twoparts[0] == "nop" {
            switch = "jmp"
        } else {
            data[i] = currentelem;
            continue;
        }

(Playground)

I guess this highlights one of the pitfalls of this approach.