Reputation: 137
I have a function that does multiple operations on two vectors. The vectors consist of structs that are later converted to tuples etc....
What I would like is to change the value of a struct in vector 1 by reference, so that the value in vector 1 is equal to the value in vector 2.
When you run the program, you will better see, what I mean. You'll get the following two output lines:
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" }] }
And what I would like is that in new_rows_after.rows.viewentry_id there is also an "xxx". But it still contains the original value "yyy".
At some point I don't seem to pass the reference correctly, but I just can't find the place.
Is there perhaps an experienced Rust expert here who can see where the error might be?
Thanks for your help.
Upvotes: 0
Views: 223
Reputation: 1129
If I may say that, your code is pretty messy. I mean, if you want us to help you, you could at least try to make it easier for us to help. One thing that generally helps is: try to reduce the length of your example code. Maybe this can help you to come up with a minimal version of your code in the future: http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/ (it is actually about minimal code examples for ICE, but minimizing Rust examples is really a general thing, if you ask me).
Now, to the actual problem. I don't really understand your full example code, so what I did, I just reduce it from a whooping 194 lines down to 43 lines, just keeping, what I assume, is your actual problem.
Also, I found your variable names rather confusing. I mean, what I understand that your problem is about, is that new
should have the value from old
but instead new
has its old value instead of the new value of old
-- seriously, that is just confusing. So, I went with a simple foo
and bar
, and here we go:
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;
}
}
Now as I understand it, in this above version, the issue is just that the id in set_bar
should be replaced with the id in set_foo
, but instead set_bar
still contains the old "bad"
as it is printed at the end of main
.
Assuming that this in deed the case: the problem is rather simple. You need to actually change the id, which is a String
. However, in the tuples
variable, you have only immutable (&
) str
s. Therefore, *dest_id = *src_id
just replaces the one reference with another and all that is only stored/modified within tuples
. The actual String
is never touched, it is not even accessible as such from tuples
.
So, what you need to do is: get your self access to a modifiable (&mut
) String
and then modify that string directly. Here you can either replace the entire string e.g. with *dest_id = src_id.to_string()
, or if you want to make sure that you really have a String
on the left-hand side, you can call a function on it that only exists on String
and not on str
like dest_id.replace_range(.., src_id)
.
So, this version of copy_ids
does what it should do:
/// 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);
}
}
Upvotes: 1