Idiomatic way to change part of struct using iterator from same struct

In the code below (playground), the borrow checker refutes the default implementation of conditional_insert because the iterator obtained from self.get_iterator() also borrows self, which prevents subsequent modification of self via self.set_flag(..).

My intend is to decouple the struct Data from its use by means of "getters" in the default trait method implementation but apparently I do something wrong and/or this is not the rust way to do it.

I suppose that I could clone self before calling get_iterator() but this seems not very efficient.

struct Data {
    vec: Vec<u32>,
    flag: u32,
}

trait Getters {
    fn get_iterator(&self) -> impl Iterator<Item=&u32> + '_;
    fn set_flag(&mut self, val: u32);
}

trait Operation : Getters {
    fn operation(&mut self){
        for val in self.get_iterator() {
            self.set_flag(*val);
        }
    }
}

impl Getters for Data {
    fn get_iterator(&self) -> impl Iterator<Item=&u32> + '_ {
        self.vec.iter()
    }

    fn set_flag(&mut self, val: u32) {
        self.flag = val;
    }
}

impl Operation for Data {}

fn main() {
    let mut data = Data {vec : vec![1,2,3], flag: 4};
    data.operation();
}

ps: I think my problem is similar to the problem discussed in this blog post but traits are not part of solution discussed there..

The fundamental problem here is that set_flag takes &mut self, which requires exclusive access to all of Data. There are a few different ways to get around this, and which one is best will depend on what your actual use case is:

  1. Use the iterator to calculate the change that should be made, and then apply that change after iteration is finished. (Playground)
  2. Provide a method that returns both the iterator and the mutable flag reference at the same time. (Playground)
  3. Wrap flag in an interior mutability primitive, so that set_flag can take &self instead. (Playground)

Getters and setters in Rust are more limited than in other languages for the reasons you're experiencing here— Methods can't be any more fine-grained in their access control than the entire object, but you can borrow different fields independently if you're working with the structure directly. For this reason, Rust code generally prefers making fields public over writing getters and setters unless there's some extra verification that needs to be made.

3 Likes

In addition to what @2e71828 said, even if you use getters/setters to decouple Data from the POV of a consumer of your crate, it's more idiomatic in Rust to not use them within your crate (due to issues such as the one in this topic).

Also similar to this blog post, though I'm not sure from your sample code if it offers anything new that would help you.


Side note: you can simplify your trait a little with no change in semantics.

 trait Getters {
-    fn get_iterator(&self) -> impl Iterator<Item=&u32> + '_;
+    fn get_iterator(&self) -> impl Iterator<Item=&u32>;
     fn set_flag(&mut self, val: u32);
 }
1 Like

Thanks for the pointers! I have restructured my struct and now it seems to work.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.