My goal is move elements out of an owned
Vec
.
fn f<F>(x: Vec<F>) -> F {
match x.as_slice() {
&[a, b] => a,
_ => panic!(),
}
}
如果
F
是复制,那没问题,因为可以简单地从切片中复制出来。当 F
不是时,切片模式似乎是不行的,因为切片是只读的。
是否存在诸如“拥有的切片”或
Vec
上的模式匹配之类的东西,以将元素移出 x
?
编辑: 我现在看到这段代码有更普遍的问题。函数
fn f<T>(x: Vec<T>) -> T {
x[0]
}
留下“
Vec
中的一个洞”,即使它随后立即掉落。这是不允许的。 这篇文章和这个讨论描述了这个问题。
这导致了更新的问题:如何正确使用
Vec<T>
来进行模式匹配?
如果你坚持模式匹配,你可以这样做:
fn f<F>(x: Vec<F>) -> F {
let mut it = x.into_iter();
match (it.next(), it.next(), it.next()) {
(Some(x0), Some(_x1), None) => x0,
_ => panic!(),
}
}
但是,如果您只想检索 2 元素向量的第一个元素(在其他情况下恐慌),我想我宁愿这样做:
fn f<F>(x: Vec<F>) -> F {
assert_eq!(x.len(), 2);
x.into_iter().next().unwrap()
}
在这种情况下,您不能将模式匹配与切片模式一起使用。
正如您在问题编辑中正确提到的那样,将值从
Vec
中移出会留下未初始化的内存。当 Vec
随后被删除时,这可能会导致未定义的行为,因为它的 Drop
实现需要释放堆内存,并可能删除每个元素。
目前无法表达您的类型参数
F
没有 Drop
实现或者从未初始化的内存强制它是安全的。
你几乎必须忘记使用切片模式的想法并更明确地写它:
fn f<F>(mut x: Vec<F>) -> F {
x.drain(..).next().unwrap()
}
如果你对模式匹配死心塌地,你可以使用
Itertools::tuples()
来匹配元组:
use itertools::Itertools; // 0.9.0
fn f<F>(mut x: Vec<F>) -> F {
match x.drain(..).tuples().next() {
Some((a, _)) => a,
None => panic!()
}
}
实现消费单个元素的一种方法是将最后一个元素与您要消费的元素交换,然后弹出最后一个元素
fn f<F>(mut x: Vec<F>) -> F {
match x.as_slice() {
[_a, _b] => {
x.swap(0, 1);
x.pop().unwrap() // returns a
},
_ => panic!(),
}
}
代码使用了一个不优雅的
unwrap
。
我在最近的 reddit 帖子 中遇到了一个很好的模式,如果您希望向量具有与您匹配的长度完全相同的情况。
Vec
通过 TryInto<[T; N]>
实现
TryFrom
的想法(自 Rust 版本 1.48 起)。这可以结合 let else
语法 来对 try 结果进行模式匹配:
let Ok([a, b]) = TryInto::<[_; 2]>::try_into(x) else { panic!("Vector has wrong length")};
如果向量可以有更多的元素,而你无论如何都想丢弃(丢弃)它们,你可以事先
truncate
它。