1

I have a structure Struct that optionally contains SubStruct. SubStruct contains a field. I would like to call the modify member method of Struct, that calls the modify_field member method of SubStruct that modifies the field field of SubStruct

This is different from other questions mentioned, as it is not modifying the field directly, but calling a member method that in turn modifies the field. Modifying the field directly has a shared solution I have seen.

struct SubStruct {
    field: u32,
}

impl SubStruct {
    fn modify_field(&mut self) {
       self.field = 2
    }
}

struct Struct {
    sub: Option<SubStruct>,
}

impl Struct {
    fn modify(&mut self) {
        if let Some(ref mut sub) = self.sub { // no reference before Some
            sub.modify_field();

            self.do_something();
        }
    }

    fn do_something(&self) {
    }
}

fn main() {
    let sub = Some(SubStruct{field: 1});
    let mut structure = Struct{ sub };

    structure.modify();

    println!("{}", structure.sub.unwrap().field);
}

(playground

I've tried many variants with no luck, with my current version I am stuck with this error:

error[E0502]: cannot borrow `*self` as immutable because `self.sub.0` is also borrowed as mutable
--> src/main.rs:20:13
   |
17 |         if let Some(ref mut sub) = self.sub { // no reference before Some
   |                          ----------- mutable borrow occurs here 
... 
20 |             self.do_something();    
   |             ^^^^ immutable borrow occurs here
21 |         }    |         - mutable borrow ends here

As you can see, it seems to be related to self.do_something() taking an immutable borrow of self, where a mutable borrow of self was already taken in the function parameter.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Andrew Mackenzie
  • 5,477
  • 5
  • 48
  • 70
  • *as it is not modifying the field directly, but calling a member method that in turn modifies the field* — The example used in the duplicate (addition) **is** a method, specifically the [`AddAssign` trait](https://doc.rust-lang.org/std/ops/trait.AddAssign.html). – Shepmaster Dec 01 '17 at 17:47
  • OK, so as ljedrez mentions below, I basically need to refactor to avoid this? – Andrew Mackenzie Dec 01 '17 at 17:50
  • Additionally, it's [frowned upon](https://meta.stackoverflow.com/q/254521/155423) to change your question, especially after it's been answered (and accepted!). – Shepmaster Dec 01 '17 at 17:50
  • The duplicate message specifically asked me to: "modify your question to clarify how it is different to others" (duplicates) and the previous edits by others had changed the error message from playground, and that didn't coincide with the code.... so I corrected it back. – Andrew Mackenzie Dec 01 '17 at 17:52
  • I copied the [**exact code you pasted here**](https://stackoverflow.com/revisions/47589295/1) into the playground, ran it, then copied the generated errors back into the question. I cannot help that the code you pasted wasn't what you meant it to be, if they differed somehow. Your *original* code and error message didn't coincide, which I fixed. Anyway, I've now marked this as a duplicate of some of the many "also borrowed" questions. – Shepmaster Dec 01 '17 at 17:54
  • Additionally, if the question you originally asked was answered, but then you discovered that you didn't ask the right question, you *ask another question*. Look at how the **accepted** answer seemingly "ignores" half of your question. The edits make the answerer look really poor, even though they answered your original question perfectly well. – Shepmaster Dec 01 '17 at 17:58

1 Answers1

1

You were pretty close:

impl Struct {
    fn modify(&mut self) {
        if let Some(ref mut sub) = self.sub { // no reference before Some
            sub.modify_field();
        }
    }
}

fn main() {
    let sub = Some(SubStruct { field: 1 });
    let mut structure = Struct { sub };

    structure.modify(); // no arguments, we are only working on self

    println!("{}", structure.sub.unwrap().field);
}

When you are mutably borrowing structure with modify(&mut self), self.sub is still an Option<SubStruct>, so destructuring it with an if let binding yields Some(sub). Since you are only mutably borrowing self, though, you need to prevent the move of the Option's contents with ref mut (as you did).

ljedrz
  • 20,316
  • 4
  • 69
  • 97
  • Thanks! That solves the example so I have accepted answer. Now let me try and apply it to my own real (more complex example)..... – Andrew Mackenzie Dec 01 '17 at 12:32
  • I was afraid so, my simplified example was too simplified.... I want to call another method of Struct and it gives the error: "error[E0502]: cannot borrow `*self` as immutable because `self.sub.0` is also borrowed as mutable" I have updated the playground code.. https://play.rust-lang.org/?gist=93bf23c21a3a54687700f1d1a66ea3c1&version=stable – Andrew Mackenzie Dec 01 '17 at 12:40
  • @AndrewMackenzie you won't be able to immutably borrow `self` in the block under that `if let` binding (i.e. while a mutable borrow is in force); you could do it just outside it, though, as long as that works for your original code. If it doesn't, I recommend filing a new question with a bit more of the original code. – ljedrz Dec 01 '17 at 12:47
  • Thanks, I feared that and don't see an easy workaround. The code now in the playground effectively reproduces the problem - without all my messiness I you are interested. I guess I'll need to rethink that code... :-( – Andrew Mackenzie Dec 01 '17 at 12:52
  • @AndrewMackenzie to be honest, it doesn't look like it needs heavy refactoring - even if this is represents the problem, some additional code could help suggest some possible solutions. – ljedrz Dec 01 '17 at 13:02
  • thanks for the help and offer of more. I created a new question that I hope captures the problem better. https://stackoverflow.com/questions/47618162/how-to-call-a-member-method-while-inside-block-with-mutable-reference-to-self – Andrew Mackenzie Dec 03 '17 at 12:04