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

这一生的挚爱 提交于 2019-12-14 01:41:49

问题


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


回答1:


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..




回答2:


If you are open to using nightly features, you can use slice patterns to bind to all the values of the array at once:

#![feature(slice_patterns)]

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([name_0, name_1]) = self.arg_names.take() {
            (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)
        }
    }
}


来源:https://stackoverflow.com/questions/38290790/how-do-i-move-string-values-from-an-array-to-a-tuple-without-copying

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!