传递引用的正确方法是什么?
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 专家可以看出错误可能在哪里?
感谢您的帮助。
如果我可以这么说,您的代码非常混乱。我的意思是,如果您希望我们帮助您,您至少可以尝试让我们更容易提供帮助。通常有帮助的一件事是:尝试减少示例代码的长度。也许这可以帮助你在未来想出一个最小版本的代码:http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
(它实际上是关于 ICE 的最小代码示例,但如果你问我的话,最小化 Rust 示例确实是一件很普遍的事情)。
问题
现在,进入实际问题。我不太理解您的完整示例代码,所以我所做的只是将它从 194 行减少到 43 行,只是保留,我假设,是您的实际问题。
此外,我发现您的变量名称相当混乱。我的意思是,据我了解,您的问题是 new
应该具有 old
的值,但 new
具有其旧值而不是 [=13= 的新值] - 严重的是,这只是令人困惑。所以,我选择了一个简单的 foo
和 bar
,然后我们开始:
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;
}
}
现在据我了解,在上面的版本中,问题只是 set_bar
中的 id 应该替换为 set_foo
中的 id,但 set_bar
仍然包含旧的 "bad"
,因为它打印在 main
.
的末尾
解决方案
假设确实如此:问题很简单。您需要实际更改 id,即 String
。但是,在 tuples
变量中,您只有不可变的 (&
) str
s。因此,*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);
}
}
我有一个函数可以对两个向量执行多项操作。向量由稍后转换为元组等的结构组成......
我想通过引用更改向量 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 专家可以看出错误可能在哪里?
感谢您的帮助。
如果我可以这么说,您的代码非常混乱。我的意思是,如果您希望我们帮助您,您至少可以尝试让我们更容易提供帮助。通常有帮助的一件事是:尝试减少示例代码的长度。也许这可以帮助你在未来想出一个最小版本的代码:http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/ (它实际上是关于 ICE 的最小代码示例,但如果你问我的话,最小化 Rust 示例确实是一件很普遍的事情)。
问题
现在,进入实际问题。我不太理解您的完整示例代码,所以我所做的只是将它从 194 行减少到 43 行,只是保留,我假设,是您的实际问题。
此外,我发现您的变量名称相当混乱。我的意思是,据我了解,您的问题是 new
应该具有 old
的值,但 new
具有其旧值而不是 [=13= 的新值] - 严重的是,这只是令人困惑。所以,我选择了一个简单的 foo
和 bar
,然后我们开始:
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;
}
}
现在据我了解,在上面的版本中,问题只是 set_bar
中的 id 应该替换为 set_foo
中的 id,但 set_bar
仍然包含旧的 "bad"
,因为它打印在 main
.
解决方案
假设确实如此:问题很简单。您需要实际更改 id,即 String
。但是,在 tuples
变量中,您只有不可变的 (&
) str
s。因此,*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);
}
}