How to resolve the conflicts between the immutable borrow and the mutable borrow?

consider the following snippet code,
the msg_builder holds the mutable reference to the member variable of MsgA, and calling the msg.check() will require the immutable borrow of MsgA.


use bytes::*;
use std::any::Any;

type Encoder<'a> = Box<dyn FnMut(&mut BytesMut) + 'a>;
type Decoder<'a> = Box<dyn FnMut(&mut Bytes) + 'a>;

pub trait IE {
    fn encode(ie: &Self, stream: &mut BytesMut)
    where
        Self: Sized;
    fn decode(stream: &mut Bytes) -> Self
    where
        Self: Sized;
    fn as_any(&self) -> &dyn Any;
}

#[derive(Debug, Clone, Copy)]
pub struct MyIEa {
    num: u32,
}

impl IE for MyIEa {
    fn decode(stream: &mut Bytes) -> Self
    where
        Self: Sized,
    {
        println!("MyIEa::decode");
        MyIEa {
            num: stream.get_u32(),
        }
    }

    fn encode(ie: &Self, stream: &mut BytesMut)
    where
        Self: Sized,
    {
        stream.put_u32(ie.num);
        println!("MyIEa::encode: {:?}", ie);
    }

    fn as_any(&self) -> &dyn Any {
        self
    }


}

pub trait SerialMsg {
    fn check_it(&self);
    fn on_build<'a: 'b, 'b>(&'a mut self, msg_builder: &mut MsgBuilder<'b>);
}

pub struct MsgA {
    ie_a: MyIEa,
}

impl SerialMsg for MsgA {
    fn on_build<'a: 'b, 'b>(&'a mut self, msg_builder: &mut MsgBuilder<'b>) {
        msg_builder.mandatory_ie(&mut self.ie_a);
    }

    fn check_it(&self) {
        let _ = self;
    }
}

pub struct MsgBuilder<'a> {
    pub msg_encoders: Vec<Encoder<'a>>,
    pub msg_decoders: Vec<Decoder<'a>>,

    pub msg_opt_encoders: Vec<Encoder<'a>>,
    pub msg_opt_decoders: Vec<Decoder<'a>>,
}

impl<'a> MsgBuilder<'a> {
    pub fn new() -> Self {
        MsgBuilder {
            msg_encoders: Vec::new(),
            msg_decoders: Vec::new(),

            msg_opt_encoders: Vec::new(),
            msg_opt_decoders: Vec::new(),
        }
    }

    pub fn mandatory_ie<X: IE + Clone + 'static>(&mut self, ie: &'a mut X) {
        let ie_cloned = ie.clone();

        // for encoder
        let encoder = move |stream: &mut BytesMut| {
            let t = ie_cloned
                .as_any()
                .downcast_ref::<X>()
                .expect("mandatory_ie, T:IE != ie: &mut dyn IE");
            X::encode(t, stream);
        };
        self.msg_encoders.push(Box::new(encoder));

        // for decoder
        let decoder = |stream: &mut Bytes| {
            let ie_x = X::decode(stream);
            ie.clone_from(&ie_x);
        };
        self.msg_decoders.push(Box::new(decoder));
    }

    pub fn optional_ie<X: IE + Clone + 'static>(&mut self, ie: &'a mut Option<X>) {
        if ie.is_some() {
            let ie = ie.clone().unwrap();

            // for encoder
            let encoder = move |stream: &mut BytesMut| {
                let t = ie
                    .as_any()
                    .downcast_ref::<X>()
                    .expect("optional_ie, T:IE != ie: &mut dyn IE");
                X::encode(t, stream);
            };
            self.msg_opt_encoders.push(Box::new(encoder));
        }

        // for decoder
        let decoder = |stream: &mut Bytes| {
            let ie_x = X::decode(stream);
            ie.clone_from(&Some(ie_x));
        };
        self.msg_opt_decoders.push(Box::new(decoder));
    }
}


fn test_ex17() {
    let mut msg = MsgA { ie_a: MyIEa { num: 100 }, };
    {
        let mut stream = Bytes::new();
        let mut stream_mut = BytesMut::new();

        let mut msg_builder = MsgBuilder::new();
        msg.on_build(&mut msg_builder);

        for mut decoder in msg_builder.msg_opt_decoders {
            decoder(&mut stream);
        }

        for mut encoeder in msg_builder.msg_opt_encoders {
            msg.check_it();
            encoeder(&mut stream_mut);
        }
    }
}


the compiler complains that:

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `msg` as immutable because it is also borrowed as mutable
   --> src/lib.rs:146:13
    |
139 |         msg.on_build(&mut msg_builder);
    |         ------------------------------ mutable borrow occurs here
...
146 |             msg.check_it();
    |             ^^^^^^^^^^^^^^ immutable borrow occurs here
...
149 |     }
    |     - mutable borrow might be used here, when `msg_builder` is dropped and runs the destructor for type `MsgBuilder<'_>`

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` due to previous error

here is the playground url:

The way your lifetime constraints are setup, the compiler thinks you might be storing the &mut msg from the call to on_build within the msg_builder -- and indeed, you are doing exactly that. So the borrow must be valid for as long as the msg_builder is valid. &mut might have been better named &excl, as the borrow is more than mutable, it's exclusive -- you can't do anything else to observe or change the value while that &mut is valid. That's why you can't call check_it.

I tried to work around it by doubling-down on more lifetimes, but ran into #70919 (however I didn't spend enough time to decide if it should compile or if it is just a broken diagnostic in this case).

The real fix is probably "don't store references in long-lived structs", and get rid of the lifetimes.

1 Like