Issue with needing to have both mutable and immutable borrrow at same time

I'm kind of new to Rust.

I have a struct and an implementation of this struct.
one of the members of the struct is a stdout_writer it's Rc<Option<RefCell<SomeNonCopyableStruct>>>
In one of the functions let's call it write I need to perform the following:

while self.writer.as_ref().unwrap().as_ref().borrow_mut().status().is_somethig() { 
    // The while performs an immutable borrow of self.
    if self.execution().is_done() == 0 {  // mutable borrow of self
         ///do something
    }
}

My issue is that on the one hand, I need to do the while, which makes the immutable borrow to self, on the other hand, I need to do the execution which needs a mutable borrow.
So I'm quite stuck here since.

Is there a design pattern to handle such a situation?

@jonh I think the condition of a while should already be dropping all temporaries after it’s evaluated.

The main issue I get with this code – assuming what you’re saying is that writer: Rc<Option<_>> – is that .is_ref() calls <Rc<…> as AsRef<_>>::as_ref not Option::as_ref. But that can be fixed in various ways, e.g.

while Option::as_ref(&self.writer).unwrap().borrow_mut().status().is_somethig()
{
    // The while performs an immutable borrow of self.
    if self.execution().is_done() == 0 { // mutable borrow of self
            // do something
    }
}

Rust Playground


Really, it’s impossible to help here any further. The questions lacks all necessary detail. The types aren’t properly explained, the error you’re getting isn’t properly shown, I’m not even entirely sure if we are talking about the issue being a compilation error in the first place.

Remember, if you’re having issue resolving a compilation error, share the compilation error. Also share a full code example that reproduces the problem, if that isn’t too difficult.

So I managed to make this compile, but It's not very clear to me why is this working.
This is the original structure: Rc<Option<RefCell<BufferedWriter>>>
The code that works is

fn write(&mut self) {
while self
            .writer
            .as_ref()
            .as_ref()
            .unwrap()
            .borrow_mut()
            .status()
            .is_none()
            {
            if self.execution().is_done() == 0 {
                   //execution
            }
        }
 }

The original error was cannot move out of a shared reference value moved due to this call pointing to the unwrap
So why is the second as_ref() solves this issue?

This is the problem I mentioned

Calling “as_ref” twice also resolves the issue, because Rc<T>: AsRef<T> (is in the prelude and) has a method called as_ref, too, which takes &Rc<T> and produces &T, so that’s giving us &Option<RefCell<BufferedWriter>> in the first step; and then that type has .as_ref() resolve to the Option::as_ref method instead, which is the one we actually wanted which takes &Option<RefCell<BufferedWriter>> and produces Option<&RefCell<BufferedWriter>>, which can support calling unwrap.

1 Like

Would you suggest a better approach?
This code feels fragile and not readable.
Most of all it's not clear straight away why is it written this way.

To make it more readable I would break it up into multiple assignment statements, with declared types if the types aren't clear, rather than a long chain of method calls.

Adding a comment with the info from Steffahn is the only way I know to clarify this.

2 Likes

I think I'd factor the while condition into a private method, self.writer_is_somethig() or whatever.

Option<Rc<T>> is usually a bit nicer than Rc<Option<T>> and saves memory, especially if the option is often None.

It really is hard to say more; there's so much context we don't have...

2 Likes

I meant more of, if there's a more rusty readable way, not in the general programming sense.
The chaining is very readable IMO when using LSP.

I actually started with Option first, but it's a lot harder to pass around, a specially when needing to mutate inside other functions.