【问题标题】:How to move values out of a vector when the vector is immediately discarded?当向量被立即丢弃时,如何将值移出向量?
【发布时间】:2019-08-28 05:26:47
【问题描述】:

我正在接收字符串向量形式的数据,并且需要使用值的子集填充结构,例如this

const json: &str = r#"["a", "b", "c", "d", "e", "f", "g"]"#;

struct A {
    third: String,
    first: String,
    fifth: String,
}

fn main() {
    let data: Vec<String> = serde_json::from_str(json).unwrap();
    let a = A {
        third: data[2],
        first: data[0],
        fifth: data[4],
    };
}

这不起作用,因为我将值移出向量。编译器认为这会使data 处于可能导致问题的未初始化状态,但因为我再也不会使用data,所以这无关紧要。

传统的解决方案是swap_remove,但它是有问题的,因为元素不是以相反的顺序访问的(假设结构是从上到下填充的)。

我现在通过执行mem::replace 并将data 设置为mut 来解决这个问题,这会使原本干净的代码变得混乱:

fn main() {
    let mut data: Vec<String> = serde_json::from_str(json).unwrap();
    let a = A {
        third: std::mem::replace(&mut data[2], "".to_string()),
        first: std::mem::replace(&mut data[0], "".to_string()),
        fifth: std::mem::replace(&mut data[4], "".to_string())
    };
}

此解决方案是否有替代方案不需要我进行所有这些replace 调用和data 不必要的mut

【问题讨论】:

  • @SvenMarnach 空字符串不分配,但是 "".to_string() 可能不是创建一个的最佳方法。我会选择std::mem::replace(&amp;mut data[2], String::new()),这可能是最好的。如果向量允许移出其元素,则它必须跟踪哪些仍然存在以释放它们,而这最终可能会降低性能。
  • @mcarton 你是对的,我的错。
  • 作为一个正在学习 Rust 的人,为什么这比 data.remove(index) 更可取,因为我们删除数据时索引会发生变化?
  • @evading 不仅只是索引会移动(这很烦人),而且每个remove 调用都必须移动向量中的所有后续数据。这是一个性能问题(尽管很可能是一个很小的问题)。

标签: rust borrow-checker


【解决方案1】:

我遇到过这种情况,我找到的最干净的解决方案是创建一个扩展:

trait Extract: Default {
    /// Replace self with default and returns the initial value.
    fn extract(&mut self) -> Self;
}

impl<T: Default> Extract for T {
    fn extract(&mut self) -> Self {
        std::mem::replace(self, T::default())
    }
}

在您的解决方案中,您可以用它替换 std::mem::replace

const JSON: &str = r#"["a", "b", "c", "d", "e", "f", "g"]"#;

struct A {
    third: String,
    first: String,
    fifth: String,
}

fn main() {
    let mut data: Vec<String> = serde_json::from_str(JSON).unwrap();
    let _a = A {
        third: data[2].extract(),
        first: data[0].extract(),
        fifth: data[4].extract(),
    };
}

这基本上是相同的代码,但它更具可读性。


如果你喜欢有趣的东西,你甚至可以写一个宏:

macro_rules! vec_destruc {
    { $v:expr => $( $n:ident : $i:expr; )+ } => {
        let ( $( $n ),+ ) = {
            let mut v = $v;
            (
                $( std::mem::replace(&mut v[$i], Default::default()) ),+
            )
        };
    }
}

const JSON: &str = r#"["a", "b", "c", "d", "e", "f", "g"]"#;

#[derive(Debug)]
struct A {
    third: String,
    first: String,
    fifth: String,
}

fn main() {
    let data: Vec<String> = serde_json::from_str(JSON).unwrap();

    vec_destruc! { data =>
        first: 0;
        third: 2;
        fifth: 4;
    };
    let a = A { first, third, fifth };

    println!("{:?}", a);
}

【讨论】:

    【解决方案2】:

    在这样的小情况下(也见于简单的命令行参数处理),我将向量的所有权转移到迭代器中并弹出所有值,保留我感兴趣的值:

    fn main() {
        let data: Vec<String> = serde_json::from_str(json).unwrap();
        let mut data = data.into_iter().fuse();
    
        let first = data.next().expect("Needed five elements, missing the first");
        let _ = data.next();
        let third = data.next().expect("Needed five elements, missing the third");
        let _ = data.next();
        let fifth = data.next().expect("Needed five elements, missing the fifth");
    
        let a = A {
            third,
            first,
            fifth,
        };
    }
    

    不过,我会挑战拥有矢量的要求。如果您正好有 5 个元素,则使用元组更简单,并且可以避免大部分所需的错误处理:

    fn main() {
        let data: (String, String, String, String, String) = serde_json::from_str(json).unwrap();
    
        let a = A {
            third: data.2,
            first: data.0,
            fifth: data.4,
        };
    }
    

    另见:

    【讨论】:

    • 我喜欢您的解决方案,因为使用Option::ok_or,可以进行适当的错误处理。
    • @FrenchBoiethios 当然。我使用了expect,因为如果没有足够的值,原始代码也会出现恐慌。
    • 这对我不起作用,因为实际上我收到了一个包含数千个字符串数组的 json 文件,我必须提取选定的片段以进行进一步处理。对元素进行排序是不可行的,千元组也不可行。
    【解决方案3】:

    另一种选择是使用Option&lt;String&gt; 的向量。这允许我们将值移出,同时跟踪已移动的值,因此它们不会与向量一起删除。

    let mut data: Vec<Option<String>> = serde_json::from_str(json).unwrap();
    let a = A {
        third: data[2].take().unwrap(),
        first: data[0].take().unwrap(),
        fifth: data[4].take().unwrap(),
    };
    

    【讨论】:

    • 我喜欢这个!谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-07
    • 2021-11-09
    相关资源
    最近更新 更多