我如何(切片)在拥有的 Vec 上与非复制元素进行模式匹配?
How can I (slice) pattern match on an owned Vec with non-Copy elements?
我的目标是将元素从拥有的 Vec
.
中移出
fn f<F>(x: Vec<F>) -> F {
match x.as_slice() {
&[a, b] => a,
_ => panic!(),
}
}
如果F
是copy,那没问题,直接copy出slice即可。当 F
不是时,切片模式似乎是不行的,因为切片是只读的。
是否有 "owned slice" 或 Vec
上的模式匹配之类的东西来将元素移出 x
?
编辑: 我现在看到这段代码有更普遍的问题。函数
fn f<T>(x: Vec<T>) -> T {
x[0]
}
离开 "a hole in a Vec
",即使它紧随其后。这是不允许的。 This post and this discussion 描述那个问题。
这导致了更新的问题:如何正确使用 Vec<T>
来进行模式匹配?
实现消耗向量的单个元素的一种方法是将最后一个元素与要消耗的元素交换,然后弹出最后一个元素
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
不优雅。
在这种情况下,您不能将模式匹配与切片模式结合使用。
正如您在问题编辑中正确提到的那样,从 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>(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
.
fn f<F>(x: Vec<F>) -> F {
match x.as_slice() {
&[a, b] => a,
_ => panic!(),
}
}
如果F
是copy,那没问题,直接copy出slice即可。当 F
不是时,切片模式似乎是不行的,因为切片是只读的。
是否有 "owned slice" 或 Vec
上的模式匹配之类的东西来将元素移出 x
?
编辑: 我现在看到这段代码有更普遍的问题。函数
fn f<T>(x: Vec<T>) -> T {
x[0]
}
离开 "a hole in a Vec
",即使它紧随其后。这是不允许的。 This post and this discussion 描述那个问题。
这导致了更新的问题:如何正确使用 Vec<T>
来进行模式匹配?
实现消耗向量的单个元素的一种方法是将最后一个元素与要消耗的元素交换,然后弹出最后一个元素
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
不优雅。
在这种情况下,您不能将模式匹配与切片模式结合使用。
正如您在问题编辑中正确提到的那样,从 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>(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()
}