I am receiving data in the form of a string vector, and need to populate a struct using a subset of the values, like this:
const json: &str = r#"["a", "b", "c", "d", "e", "f", "g"]"#;
struct A {
third: String,
first: String,
fifth: String,
}
fn main() {
let data: Vec<String> = serde_json::from_str(json).unwrap();
let a = A {
third: data[2],
first: data[0],
fifth: data[4],
};
}
This doesn't work because I'm moving values out of the vector. The compiler believes that this leaves data
in an uninitialized state that can cause problems, but because I never use data
again, it shouldn't matter.
The conventional solution is swap_remove
, but it is problematic because the elements are not accessed in reverse order (assuming the structure is populated top to bottom).
I solve this now by doing a mem::replace
and having data
as mut
, which clutters this otherwise clean code:
fn main() {
let mut data: Vec<String> = serde_json::from_str(json).unwrap();
let a = A {
third: std::mem::replace(&mut data[2], "".to_string()),
first: std::mem::replace(&mut data[0], "".to_string()),
fifth: std::mem::replace(&mut data[4], "".to_string())
};
}
Is there an alternative to this solution that doesn't require me to have all these replace
calls and data
unnecessarily mut
?
I've been in this situation, and the cleanest solution I've found was to create an extension:
trait Extract: Default {
/// Replace self with default and returns the initial value.
fn extract(&mut self) -> Self;
}
impl<T: Default> Extract for T {
fn extract(&mut self) -> Self {
std::mem::replace(self, T::default())
}
}
And in your solution, you can replace the std::mem::replace
with it:
const JSON: &str = r#"["a", "b", "c", "d", "e", "f", "g"]"#;
struct A {
third: String,
first: String,
fifth: String,
}
fn main() {
let mut data: Vec<String> = serde_json::from_str(JSON).unwrap();
let _a = A {
third: data[2].extract(),
first: data[0].extract(),
fifth: data[4].extract(),
};
}
That's basically the same code, but it is much more readable.
If you like funny things, you can even write a macro:
macro_rules! vec_destruc {
{ $v:expr => $( $n:ident : $i:expr; )+ } => {
let ( $( $n ),+ ) = {
let mut v = $v;
(
$( std::mem::replace(&mut v[$i], Default::default()) ),+
)
};
}
}
const JSON: &str = r#"["a", "b", "c", "d", "e", "f", "g"]"#;
#[derive(Debug)]
struct A {
third: String,
first: String,
fifth: String,
}
fn main() {
let data: Vec<String> = serde_json::from_str(JSON).unwrap();
vec_destruc! { data =>
first: 0;
third: 2;
fifth: 4;
};
let a = A { first, third, fifth };
println!("{:?}", a);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With