8

我以字符串向量的形式接收数据,并且需要使用值的子集填充结构,如下所示

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,但这是有问题的,因为元素不是以相反的顺序访问的(假设结构是从上到下填充的)。

我现在通过执行 amem::replace和拥有dataas来解决这个问题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

4

3 回答 3

5

我一直在这种情况下,我发现最干净的解决方案是创建一个扩展:

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);
}
于 2019-08-28T13:40:47.257 回答
3

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

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,
    };
}

也可以看看:

于 2019-08-28T14:16:50.297 回答
1

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

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(),
};
于 2019-08-28T18:58:29.563 回答