Issue
I have collection which is a field in a struct in some module. I want to update all the values in the collection from another module.
I wrote some code to mimic what I want to achieve. It's shortened a bit, but I think it has all needed parts. There is no struct holding the collection in this code, but imagine this is a getter which returns the collection. I added in comments how I think it should look.
pub mod pos {
use std::cmp::{Ordering, PartialEq};
#[derive(PartialOrd, PartialEq, Eq, Hash, Debug, Copy, Clone)]
pub struct Pos {
pub x: i32,
pub y: i32,
}
#[allow(dead_code)]
impl Pos {
pub fn of(x: i32, y: i32) -> Self {
Self { x, y }
}
pub fn offset(&mut self, pos: &Self) -> Self {
self.x += pos.x;
self.y += pos.y;
*self
}
}
impl Ord for Pos {
fn cmp(&self, other: &Self) -> Ordering {
if self.x < other.x {
Ordering::Less
} else if self.eq(other) {
Ordering::Equal
} else {
Ordering::Greater
}
}
}
}
mod test {
use crate::pos::Pos;
use std::collections::BTreeSet;
#[test]
fn test_iterators() {
let mut data_in_some_strct: BTreeSet<Pos> = BTreeSet::new();
data_in_some_strct.insert(Pos::of(1, 1));
data_in_some_strct.insert(Pos::of(2, 2));
data_in_some_strct.insert(Pos::of(3, 3));
data_in_some_strct.insert(Pos::of(4, 4));
// mimic getter call ( get_data(&mut self) -> &BTreeSet<Pos> {...}
// let set = data_in_some_strct; // works, but not a reference
let set = &data_in_some_strct; // doesn't work, How to adjust code to make it work??
data_in_some_strct = set
.into_iter()
.map(|mut p| p.offset(&Pos::of(1, 0)))
.inspect(|p| println!("{:?}", *p))
.collect();
assert_eq!(data_in_some_strct.contains(&Pos::of(2, 1)), true);
assert_eq!(data_in_some_strct.contains(&Pos::of(3, 2)), true);
assert_eq!(data_in_some_strct.contains(&Pos::of(4, 3)), true);
assert_eq!(data_in_some_strct.contains(&Pos::of(5, 4)), true);
}
}
error[E0596]: cannot borrow `*p` as mutable, as it is behind a `&` reference
--> src/lib.rs:56:26
|
56 | .map(|mut p| p.offset(&Pos::of(1, 0)))
| - ^ `p` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| help: consider changing this to be a mutable reference: `&mut pos::Pos`
I managed to make it work without borrowing, but I would like to make it work with borrowing. I guess there is more then one way to achieve it. Comments to help my Rust brain dendrites connect are welcome.
Solution
You can't mutate items that are part of a HashSet
or BTreeSet
because the value of the items determines how they are stored and accessed. If you mutate them then, as Stargateur mentioned, you would break the mechanics of the collection. In the case of a HashSet
, you would change the hash of the item, which determines the location where the data is stored. In the case of a BTreeSet
, the algorithm is based on how the items are sorted.
You are able to do it by taking ownership because you consume the original set and produce a new, well-formed one. You can't take ownership of a borrowed value because that would leave behind a dangling pointer, which Rust will not let you do.
One possible solution is to temporarily replace the original set with an empty one. Then you can take ownership of its contents, as in your working code, and finally write the newly updated set over the original:
let set = std::mem::replace(&mut data_in_some_strct, BTreeSet::new());
data_in_some_strct = set.into_iter()
.map(|mut p| p.offset(&Pos::of(1,0)))
.inspect(|p| println!("{:?}", *p))
.collect();
Answered By - Peter Hall Answer Checked By - Katrina (PHPFixing Volunteer)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.