如何在立即丢弃向量时将值移出向量?
How to move values out of a vector when the vector is immediately discarded?
我正在接收字符串向量形式的数据,需要使用值的子集填充结构,例如 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],
};
}
这不起作用,因为我正在将值移出向量。编译器认为这会使 data
处于未初始化状态,这可能会导致问题,但因为我再也没有使用过 data
,所以这应该无关紧要。
传统的解决方案是swap_remove
,但它是有问题的,因为元素不是以相反的顺序访问的(假设结构是从上到下填充的)。
我现在通过执行 mem::replace
并将 data
设为 mut
来解决这个问题,这会使原本干净的代码变得混乱:
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())
};
}
有没有不需要我进行所有这些 replace
调用和 data
不必要的 mut
的替代解决方案?
我遇到过这种情况,我找到的最干净的解决方案是创建一个扩展:
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())
}
}
在您的解决方案中,您可以用它替换 std::mem::replace
:
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(),
};
}
这基本上是相同的代码,但可读性更高。
如果你喜欢有趣的东西,你甚至可以写一个宏:
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);
}
在像这样的小情况下(也见于简单的命令行参数处理),我将向量的所有权转移到迭代器中并弹出所有值,保留我感兴趣的值:
fn main() {
let data: Vec<String> = serde_json::from_str(json).unwrap();
let mut data = data.into_iter().fuse();
let first = data.next().expect("Needed five elements, missing the first");
let _ = data.next();
let third = data.next().expect("Needed five elements, missing the third");
let _ = data.next();
let fifth = data.next().expect("Needed five elements, missing the fifth");
let a = A {
third,
first,
fifth,
};
}
但是,我会挑战拥有矢量的要求。如果恰好有 5 个元素,使用元组会更简单,并且可以避免很多所需的错误处理:
fn main() {
let data: (String, String, String, String, String) = serde_json::from_str(json).unwrap();
let a = A {
third: data.2,
first: data.0,
fifth: data.4,
};
}
另请参阅:
另一种选择是使用Option<String>
的向量。这允许我们将值移出,同时跟踪已移动的值,因此它们不会随向量一起删除。
let mut data: Vec<Option<String>> = serde_json::from_str(json).unwrap();
let a = A {
third: data[2].take().unwrap(),
first: data[0].take().unwrap(),
fifth: data[4].take().unwrap(),
};
我正在接收字符串向量形式的数据,需要使用值的子集填充结构,例如 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],
};
}
这不起作用,因为我正在将值移出向量。编译器认为这会使 data
处于未初始化状态,这可能会导致问题,但因为我再也没有使用过 data
,所以这应该无关紧要。
传统的解决方案是swap_remove
,但它是有问题的,因为元素不是以相反的顺序访问的(假设结构是从上到下填充的)。
我现在通过执行 mem::replace
并将 data
设为 mut
来解决这个问题,这会使原本干净的代码变得混乱:
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())
};
}
有没有不需要我进行所有这些 replace
调用和 data
不必要的 mut
的替代解决方案?
我遇到过这种情况,我找到的最干净的解决方案是创建一个扩展:
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())
}
}
在您的解决方案中,您可以用它替换 std::mem::replace
:
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(),
};
}
这基本上是相同的代码,但可读性更高。
如果你喜欢有趣的东西,你甚至可以写一个宏:
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);
}
在像这样的小情况下(也见于简单的命令行参数处理),我将向量的所有权转移到迭代器中并弹出所有值,保留我感兴趣的值:
fn main() {
let data: Vec<String> = serde_json::from_str(json).unwrap();
let mut data = data.into_iter().fuse();
let first = data.next().expect("Needed five elements, missing the first");
let _ = data.next();
let third = data.next().expect("Needed five elements, missing the third");
let _ = data.next();
let fifth = data.next().expect("Needed five elements, missing the fifth");
let a = A {
third,
first,
fifth,
};
}
但是,我会挑战拥有矢量的要求。如果恰好有 5 个元素,使用元组会更简单,并且可以避免很多所需的错误处理:
fn main() {
let data: (String, String, String, String, String) = serde_json::from_str(json).unwrap();
let a = A {
third: data.2,
first: data.0,
fifth: data.4,
};
}
另请参阅:
另一种选择是使用Option<String>
的向量。这允许我们将值移出,同时跟踪已移动的值,因此它们不会随向量一起删除。
let mut data: Vec<Option<String>> = serde_json::from_str(json).unwrap();
let a = A {
third: data[2].take().unwrap(),
first: data[0].take().unwrap(),
fifth: data[4].take().unwrap(),
};