I have a problem I found a solution to but it is so ugly that there must be a better "idiomatic" way to solve it (I'm from a C++/Java background so I still fell the urge to make everything classes ...)
I have a method/functions which gets a problem with referencing mutable variables in the struct together with an mutable "self" reference.
The structure is something like this
pub struct A { map: BtreeMap<...>, }
impl A {
fn helper1(&self, ...) { ... }
fn helper2(&mut self, &mut map_entry ...) {
// Some work that adjusts struct entry "map_entry"
self.helper1( &self, ...);
}
fn func1(&mut self, ...) {
// Get some reference to a specific BTreeMap entry last_entry()
self.helper2( self.map.get_last() );
// Cleaning up etc.
}
fn func2(&mut self, ...) {
// Get some other reference to a specific BTreeMap entry first_entry()
self.helper2( self.map.get_first() );
// Cleaning up etc.
}
fn func3(&mut self, ...) {
self.helper2( ... some other calculations to determine map entry ...);
}
}
Now, this will (of course) fail with a "Cannot borrow *self
as mutable more than once ..." in func1,2,3()
as I have both self and the variable from the struct (entry from the BTreeMap) as mutable. I fully (I think ...) understand the problem.
This reason for this error is me trying to break it down in multiple methods as a lot of the work can be factored out in the helper2() method which can be used from func1,2,3()
and also wanting to keep it as a "class" method.
I can fix this in two ways
- duplicate code to avoid having to make calls to other methods and thereby avoiding the "self" borrow issue
- make all the helper methods into static functions to be called as
A::helper2()
and not having a "self" being borrowed more than one time in thefunc1,2,3()
as they have no need to access any internal struct variables.
As I don't want to duplicate code I solved it with Method 2).
However, this seems quite "ugly" to me having to mix static functions with "class/object" functions.
Is it possible to give some generic advise (without having to go into more specific details in my case) about the idiomatic way to handle this more generic issue. It feels like this use case or situation wouldn't be that uncommon?
I have a problem I found a solution to but it is so ugly that there must be a better "idiomatic" way to solve it (I'm from a C++/Java background so I still fell the urge to make everything classes ...)
I have a method/functions which gets a problem with referencing mutable variables in the struct together with an mutable "self" reference.
The structure is something like this
pub struct A { map: BtreeMap<...>, }
impl A {
fn helper1(&self, ...) { ... }
fn helper2(&mut self, &mut map_entry ...) {
// Some work that adjusts struct entry "map_entry"
self.helper1( &self, ...);
}
fn func1(&mut self, ...) {
// Get some reference to a specific BTreeMap entry last_entry()
self.helper2( self.map.get_last() );
// Cleaning up etc.
}
fn func2(&mut self, ...) {
// Get some other reference to a specific BTreeMap entry first_entry()
self.helper2( self.map.get_first() );
// Cleaning up etc.
}
fn func3(&mut self, ...) {
self.helper2( ... some other calculations to determine map entry ...);
}
}
Now, this will (of course) fail with a "Cannot borrow *self
as mutable more than once ..." in func1,2,3()
as I have both self and the variable from the struct (entry from the BTreeMap) as mutable. I fully (I think ...) understand the problem.
This reason for this error is me trying to break it down in multiple methods as a lot of the work can be factored out in the helper2() method which can be used from func1,2,3()
and also wanting to keep it as a "class" method.
I can fix this in two ways
- duplicate code to avoid having to make calls to other methods and thereby avoiding the "self" borrow issue
- make all the helper methods into static functions to be called as
A::helper2()
and not having a "self" being borrowed more than one time in thefunc1,2,3()
as they have no need to access any internal struct variables.
As I don't want to duplicate code I solved it with Method 2).
However, this seems quite "ugly" to me having to mix static functions with "class/object" functions.
Is it possible to give some generic advise (without having to go into more specific details in my case) about the idiomatic way to handle this more generic issue. It feels like this use case or situation wouldn't be that uncommon?
Share Improve this question asked Feb 17 at 15:28 JohanJohan 3951 gold badge4 silver badges12 bronze badges 5 |1 Answer
Reset to default 0Whenever I encounter this kind of problem, I knows I don't follow the rust way of thinking, I think the best option is to refactor your struct definition to separate the BTreeMap
from the struct A
to avoid self reference.
struct B {
// ...
}
struct Map(BTreeMap<...>)
that way, inside B
's function, you can &mut self
all the way down, and take &mut Map
, &mut Option<V>
, &mut V
, down each deeper call.
struct B {
my_field: i32,
}
struct Map(BTreeMap<String, i32>);
// that way you can have custom impl for this specific type of map
// avoid mix up with other string,i32 map
impl B {
fn top(&mut self, map: &mut Map) {
self.my_field += 1;
self.mid(map.0.get_mut("foo"));
}
fn mid(&mut self, v: Option<&mut i32>) {
self.my_field += 1;
self.bot(v.unwrap());
}
fn bot(&mut self, v: &mut i32) {
self.my_field += 1;
}
}
if you really needed to store both the map and the data at the same struct just make a struct A {b:B,m:Map}
.
If you cannot change the struct
here's an (crazy?) idea to refactor the snippet.
use impl Fn(&mut BTreeMap<K, V>) -> &mut V
getter as arg for helper2
.
that way you can get the reference to value within helper2
, and don't have to worries about multiple borrow.
you can provide capture, fn
or Self::fn
as argument.
Example
use std::collections::BTreeMap;
fn get_bar(m: &mut BTreeMap<String, i32>) -> &mut i32 {
m.get_mut("bar").unwrap()
}
#[derive(Debug)]
pub struct A {
my_field: i32,
map: BTreeMap<String, i32>,
}
impl A {
fn helper1(&self) {
println!("........helper 1");
}
fn helper2(&mut self, getter: impl Fn(&mut BTreeMap<String, i32>) -> &mut i32) {
println!("....helper 2");
let v = getter(&mut self.map);
// self.map.get_mut("foo"); // <-- this will error, since multiple borrow
// change the value
*v += 1;
let _ = v; // drop of value reference v
self.map.get("foo"); // mut borrow possible again
// mut reference of self
self.my_field += 1;
self.helper1()
}
// use capture
fn fun1(&mut self) {
println!("fun 1");
self.helper2(|m| m.get_mut("foo").unwrap());
}
// use fn
fn fun2(&mut self) {
println!("fun 2");
self.helper2(get_bar);
}
// use struct fn
fn fun3(&mut self) {
println!("fun 3");
self.helper2(Self::get_baz);
}
fn get_baz(m: &mut BTreeMap<String, i32>) -> &mut i32 {
m.get_mut("baz").unwrap()
}
}
fn main() {
let mut a: A = A {
my_field: 0,
map: BTreeMap::from_iter(
[
("foo".to_string(), 69),
("bar".to_string(), 42),
("baz".to_string(), 123),
]
.into_iter(),
),
};
println!("{:?}\n", a);
a.fun1();
println!("{:?}\n", a);
a.fun2();
println!("{:?}\n", a);
a.fun3();
println!("{:?}\n", a);
}
A { my_field: 0, map: {"bar": 42, "baz": 123, "foo": 69} }
fun 1
....helper 2
........helper 1
A { my_field: 1, map: {"bar": 42, "baz": 123, "foo": 70} }
fun 2
....helper 2
........helper 1
A { my_field: 2, map: {"bar": 43, "baz": 123, "foo": 70} }
fun 3
....helper 2
........helper 1
A { my_field: 3, map: {"bar": 43, "baz": 124, "foo": 70} }
self
if not needed is quite idiomatic. Perhaps even make them free functions. – Chayim Friedman Commented Feb 17 at 15:33self
, you can pass the fields as separate parameters. Alternatively, a trick you can use is to remove the entry from the map, then re-insert it. – Chayim Friedman Commented Feb 17 at 15:34Self::helper2()
instead ofA::helper2()
, which is significantly shorter ifA
has a long name and/or a bunch of generics. (Also, it makes it crystal-clear that you're invoking a non-self-taking method on the same type.) – user4815162342 Commented Feb 17 at 15:50