【问题标题】:How can I (slice) pattern match on an owned Vec with non-Copy elements?如何在拥有的 Vec 上使用非复制元素(切片)模式匹配?
【发布时间】:2020-09-15 15:37:45
【问题描述】:

我的目标是将元素移出拥有的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 中的一个洞”,即使它随后被丢弃。这是不允许的。 This postthis discussion 描述了这个问题。

这导致了更新的问题:Vec&lt;T&gt; 如何被正确地用于进行模式匹配?

【问题讨论】:

  • 你的函数返回F,这意味着它必须移动、复制或克隆项目。
  • 假设您可以从向量中移动一个元素,您希望向量处于什么状态?
  • @PeterHall 完全正确:我想搬家(问题已编辑)。
  • @E_net4theRustacean 我希望删除 Vec

标签: rust pattern-matching slice ownership


【解决方案1】:

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

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

【讨论】:

  • 这实际上是我最终做的。它看起来不太好,但我认为对于我的用例来说,这是最糟糕的解决方案。
【解决方案2】:

在这种情况下,您不能将模式匹配与切片模式一起使用。

正如您在问题编辑中正确提到的那样,将值移出 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!()
    }
}

【讨论】:

  • 我开始意识到这一点。正如您所演示的,返回单个值的简单函数可以很容易地很好地表达。然而,问题是我的函数看起来更像this
  • @vandenheuvel 我更新了问题以使用drain,我认为这可以解决那里的问题。它产生一个迭代器拥有的值的迭代器。
  • 我现在正在研究vec_drain_as_slice 功能,这可能只是我的救命之恩...
  • 我认为您会遇到与Drain.as_slice() 相同的问题。您也无法将项目移出切片。
  • 如果你可以匹配元组而不是切片模式,我的编辑应该会有所帮助。
【解决方案3】:

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

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

【讨论】:

  • +1 表示贡献的创造力,但它并没有解决模式匹配的更大问题......
  • 这是在标准库中:x.swap_remove(0)
猜你喜欢
  • 1970-01-01
  • 2016-11-27
  • 1970-01-01
  • 2015-04-02
  • 2016-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多