rust I(切片)模式如何在具有非Copy元素的所有Vec上匹配?

holgip5t  于 2023-04-12  发布在  其他
关注(0)|答案(4)|浏览(213)

我的目标是将元素移出拥有的Vec

fn f<F>(x: Vec<F>) -> F {
    match x.as_slice() {
        &[a, b] => a,
        _ => panic!(),
    }
}

如果F是copy,那就没有问题,因为你可以简单地从slice中复制出来。如果F不是,slice模式似乎是不可行的,因为slice是只读的。
有没有一个“拥有的切片”,或者Vec上的模式匹配,可以将元素移出x

**编辑:**我现在看到这段代码有更普遍的问题。函数

fn f<T>(x: Vec<T>) -> T {
    x[0]
}

留下“a hole in a Vec“,即使它在之后被丢弃。这是不允许的。这篇文章和this discussion描述了这个问题。
这就引出了更新的问题:如何正确使用Vec<T>来进行模式匹配?

hmtdttj4

hmtdttj41#

如果你坚持模式匹配,你可以这样做:

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()
}
hivapdat

hivapdat2#

在这种情况下,不能对切片模式使用模式匹配。
正如您在问题编辑中正确提到的,将值移出Vec会使其保留未初始化的内存。这可能会在随后删除Vec时导致Undefined Behaviour,因为其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!()
    }
}
ncecgwcz

ncecgwcz3#

实现消费向量的单个元素的一种方法是将最后一个元素与要消费的元素交换,然后弹出最后一个元素

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,这并不优雅。

eni9jsuy

eni9jsuy4#

我在最近的reddit post中遇到了一个很好的模式,它适用于您希望向量具有与您匹配的长度完全相同的情况。
利用Vec通过TryFrom实现TryInto<[T; N]>的想法(自Rust版本1.48起)。这可以与let else syntax结合起来对try结果进行模式匹配:

let Ok([a, b]) = TryInto::<[_; 2]>::try_into(x) else { panic!("Vector has wrong length")};

如果vector可以有更多的元素,并且无论如何都要丢弃(drop)它们,那么可以预先truncate它。

相关问题