传递引用的正确方法是什么?

What is the correct way to pass references?

我有一个函数可以对两个向量执行多项操作。向量由稍后转换为元组等的结构组成......

我想通过引用更改向量 1 中结构的值,以便向量 1 中的值等于向量 2 中的值。

当你运行这个程序时,你会更好地理解我的意思。 您将获得以下两行输出:

new_rows after: IndexSet { rows: [IndexRow { is_old_idx: false, hash: "1", value: [], name: "", viewentry_id: "yyy" }] }

old_rows after: IndexSet { rows: [IndexRow { is_old_idx: true, hash: "1", value: [], name: "", viewentry_id: "xxx" }] }

我想要的是 new_rows_after.rows.viewentry_id 中也有一个“xxx”。但它仍然包含原始值“yyy”。

有时我似乎没有正确传递引用,但我就是找不到地方。

这里是否有经验丰富的 Rust 专家可以看出错误可能在哪里?

感谢您的帮助。

Playground link

如果我可以这么说,您的代码非常混乱。我的意思是,如果您希望我们帮助您,您至少可以尝试让我们更容易提供帮助。通常有帮助的一件事是:尝试减少示例代码的长度。也许这可以帮助你在未来想出一个最小版本的代码:http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/ (它实际上是关于 ICE 的最小代码示例,但如果你问我的话,最小化 Rust 示例确实是一件很普遍的事情)。


问题

现在,进入实际问题。我不太理解您的完整示例代码,所以我所做的只是将它从 194 行减少到 43 行,只是保留,我假设,是您的实际问题。

此外,我发现您的变量名称相当混乱。我的意思是,据我了解,您的问题是 new 应该具有 old 的值,但 new 具有其旧值而不是 [=13= 的新值] - 严重的是,这只是令人困惑。所以,我选择了一个简单的 foobar,然后我们开始:

fn main() {
    let dummy_foo = vec![IndexRow {
        id: "good".to_string(),
    }];
    let dummy_bar = vec![IndexRow {
        id: "bad".to_string(),
    }];
    let set_foo = IndexSet { rows: dummy_foo };
    let mut set_bar = IndexSet { rows: dummy_bar };

    // Should copy ids from set_foo to set_bar
    copy_ids(&mut set_bar, &set_foo);

    // Here set_bar still contains "bad"
    println!("set_bar: {:?}", set_bar);
}

#[derive(Debug)]
pub struct IndexRow {
    pub id: String,
}

#[derive(Debug)]
pub struct IndexSet {
    pub rows: Vec<IndexRow>,
}

/// Copy ids from `src` to `dest`
pub fn copy_ids<'a, 'b>(dest: &'a mut IndexSet, src: &'b IndexSet) {
    // Create tuples each with a dest and src entry
    let mut tuples: Vec<(&str, &str)> = dest
        .rows
        .iter()
        .zip(src.rows.iter())
        .map(|(d, s)| (d.id.as_str(), s.id.as_str()))
        .collect();

    for t in tuples.iter_mut() {
        let (ref mut dest_id, src_id) = t;
        // Override dest with src
        *dest_id = *src_id;
    }
}

playground

现在据我了解,在上面的版本中,问题只是 set_bar 中的 id 应该替换为 set_foo 中的 id,但 set_bar 仍然包含旧的 "bad",因为它打印在 main.

的末尾

解决方案

假设确实如此:问题很简单。您需要实际更改 id,即 String。但是,在 tuples 变量中,您只有不可变的 (&) strs。因此,*dest_id = *src_id 只是用另一个引用替换了一个引用,而 tuples 中的所有内容都只有 stored/modified。实际的 String 从未被触及,甚至无法从 tuples.

访问它

因此,您需要做的是:让您自己访问可修改的 (&mut) String,然后直接修改 that 字符串。在这里您可以替换整个字符串,例如使用 *dest_id = src_id.to_string(),或者如果您想确保左侧确实有一个 String,您可以在其上调用一个仅存在于 String 上而不存在于 String 上的函数str 喜欢 dest_id.replace_range(.., src_id).

所以,这个版本的 copy_ids 做了它应该做的事情:

/// Copy ids from `src` to `dest`
pub fn copy_ids<'a, 'b>(dest: &'a mut IndexSet, src: &'b IndexSet) {
    // Create tuples each with a dest and src entry
    let tuples: Vec<(&mut String, &str)> = dest
        .rows
        .iter_mut()
        .zip(src.rows.iter())
        .map(|(d, s)| (&mut d.id, s.id.as_str()))
        .collect();

    // Override dest with src
    for (dest_id, src_id) in tuples.into_iter() {
        // Replace the content of the String
        dest_id.replace_range(.., src_id);
    }
}

full example on playground