【问题标题】:how to push into a vector a variable's resource but keep it alive如何将变量的资源推入向量但使其保持活动状态
【发布时间】:2021-07-11 21:02:09
【问题描述】:

我有一个关于如何将move 变量转换为向量但又不破坏该变量的问题,因为它必须在循环中重复使用。下面说明我的意思:

type Letter = char;
type LetterSet = Vec<Letter>;

fn main() {
    let mut v = Vec::<LetterSet>::new();
    let mut letterset: LetterSet = LetterSet::new(); 

    for i in 0..5 {
        let a = (48 + i as u8) as char;
        letterset.push(a);
        if i % 2 == 1 {
            let b = (65 + i as u8) as char;
            letterset.push(b); 
        } else {
            let c = (97 + i as u8) as char;
            letterset.push(c);
        }
        v.push(letterset.clone());
        letterset.clear();
    }

    for letterset in v {
        for letter in letterset {
            print!("{} ", letter);
        }
        println!("");
    } 
}

上面的代码运行。但是,我想避免clone in

        v.push(letterset.clone());
        letterset.clear();

因为letterset 无论如何都会被清除。相反,我想将它的内容移动到 v 中,但要让它保持活动状态,因为 letterset 必须用于下一次迭代。

我试图删除.clone(),但显然这会导致“移动后借用”错误。我想知道怎么做,谢谢。

【问题讨论】:

  • 既然我们谈论的是Vec&lt;char&gt;.clone() 几乎肯定会编译成一个分配(这是你无法避免的)和一个memcpy()。我真的没有理由避免使用.clone()。要回答这个问题:您可以v.push(letterset.drain(..).collect()),这会将拥有的元素从letterset 移动到新的Vec 中而不复制它们,留下一个已清除的letterset。同样,这仅在Letter 比简单的char 更重时才有意义。
  • @user2722968 感谢您的评论。我对clone(如果正确)的理解是,它将在letterset 被清除之前堆分配和复制资源。是的,实际上Letter 比玩具示例重得多。
  • 在这种情况下,letterset.clone() 将克隆Vec 及其所有内容,然后丢弃原件;同样,如果是char,这是非常便宜的,不值得麻烦。 .drain()-thingy 将避免克隆,因为它将 Letter 直接移动到其新位置。也看看下面的答案。

标签: rust clone move vec borrow


【解决方案1】:

为什么不在每次迭代时创建一个新的 LetterSet?移动 Vec 只是移动对堆的引用,它非常便宜。

如果您真的想重复使用相同的 var,您可以使用 std::mem::take,它将返回 Vec 并默认替换原始变量(空 vec) - 这具有相同的行为,因为您正在制作每次都有新的 Vec。

v.push(std::mem::take(&mut letterset));

你也可以跳过.clear(),因为它已经是空的了。

【讨论】:

  • 请注意,将Vec 替换为其Default 而不是对其调用.clear() 将使用新的空分配,而不是重新使用旧容量。
  • 旧容量被移动到v后仍在使用 - 它没有复制它。
  • 重点是lettershop 被推送到,而v 中的Vec 可能不是。所以在take() 之后,lettershop.push 的下一个 all 肯定会导致分配。
  • 它没有选择分配,v 是一个Vec&lt;Vec&lt;char&gt;&gt; 并且letterset (Vec&lt;char&gt;) 使用的存储正在移动,因为整个 Vec 被推送到v (注意:它没有附加,整个向量是元素)。内存必须存在于某个地方,要么被移动到内部 Vec,要么在被清除之前被克隆。
  • @Mathieu Rene,感谢您的建议。 letterset 必须在循环之外;它用作循环多次迭代的缓存。
【解决方案2】:

您可以简单地将letterset 初始化移动到内部循环中:

for i in 0..5 {
    let mut letterset: LetterSet = LetterSet::new(); 
    let a = (48 + i as u8) as char;
    letterset.push(a);
    if i % 2 == 1 {
        let b = (65 + i as u8) as char;
        letterset.push(b); 
    } else {
        let c = (97 + i as u8) as char;
        letterset.push(c);
    }
    v.push(letterset);
}

或者完全消除它:

for i in 0..5 {
    let a = (48 + i as u8) as char;
    let b = (65 + i as u8) as char;
    let c = (97 + i as u8) as char;
    v.push(vec![a, if i % 2 == 1 { b } else { c }]);
}

【讨论】:

  • 谢谢@orlp。我无法在循环中初始化 letterset。这个例子过于简单了。实际上,我在一个键排序表上工作,其中多行可能具有相同的键,我需要处理每个键的数据,而不是每一行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-09
  • 1970-01-01
  • 2013-06-15
  • 1970-01-01
  • 2016-08-29
  • 1970-01-01
相关资源
最近更新 更多