Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I move String values from an array to a tuple without copying?

I have a fixed size array of Strings: [String; 2]. I want to turn it into a (String, String). Can I do this without copying the values?

The piece of code that I'm working on in particular is the following:

let (basis, names_0, names_1) = if let Some(names) = self.arg_name {
    (ComparisonBasis::Name, names[0], names[1])
} else {
    (ComparisonBasis::File, self.arg_file[0], self.arg_file[1])
};

types:

self.arg_name: Option<[String; 2]>
self.arg_file: Vec<String>

Right now I'm getting errors

cannot move out of type `[std::string::String; 2]`, a non-copy fixed-size array [E0508]

and

cannot move out of indexed content [E0507]

for the two arms of the if

like image 394
Apanatshka Avatar asked Oct 16 '25 14:10

Apanatshka


1 Answers

You've omitted a fair amount of context, so I'm taking a guess at a few aspects. I'm also hewing a little closer to the question you asked, rather than the vaguer one implied by your snippets.

struct NeverSpecified {
    arg_names: Option<[String; 2]>,
    arg_file: Vec<String>,
}

impl NeverSpecified {
    fn some_method_i_guess(mut self) -> (String, String) {
        if let Some(mut names) = self.arg_names {
            use std::mem::replace;
            let name_0 = replace(&mut names[0], String::new());
            let name_1 = replace(&mut names[1], String::new());
            (name_0, name_1)
        } else {
            let mut names = self.arg_file.drain(0..2);
            let name_0 = names.next().expect("expected 2 names, got 0");
            let name_1 = names.next().expect("expected 2 names, got 1");
            (name_0, name_1)
        }
    }
}

I use std::mem::replace to switch the contents of the array, whilst leaving it in a valid state. This is necessary because Rust won't allow you to have a "partially valid" array. There are no copies or allocations involved in this path.

In the other path, we have to pull elements out of the vector by hand. Again, you can't just move values out of a container via indexing (this is actually a limitation of indexing overall). Instead, I use Vec::drain to essentially chop the first two elements out of the vector, then extract them from the resulting iterator. To be clear: this path doesn't involve any copies or allocations, either.

As an aside, those expect methods shouldn't ever be triggered (since drain does bounds checking), but better paranoid than sorry; if you want to replace them with unwrap() calls instead, that should be fine..

like image 198
DK. Avatar answered Oct 19 '25 08:10

DK.