How to async trigger mutable reference with no return

Hi Rust Gurus,

I am pretty new to rust coming from other programming language background. Not very familiar with lifetime issues. I have third-party library GitHub - stepfunc/dnp3: Rust implementation of DNP3 (IEEE 1815) with idiomatic bindings for C, .NET, C++, and Java. I want to fire async fn that run update_feedback with the background not blocking while receiving a control. Can you guys shed me light what I am doing wrong.

struct Dnp3simulatorControlHandler{
    arc_signals_map: Arc<HashMap<String, Arc<Signal>>>,
    arc_state_map: Arc<DigStateMap>,
}
impl Dnp3simulatorControlHandler {

    async fn async_update_feedback<'a, F>(
        &'a mut self, 
        database_handle: &'a mut DatabaseHandle,
        index: u16,
        qualityflags: Flags,
        eventtime: Time,
        bit0: bool,
        bit1: bool,
        is_double_bit: bool,
        update_fn: F,
    )
    where
        F: for<'b> FnOnce(&'b mut DatabaseHandle, u16, Flags, Time, bool, bool, bool) + Send + 'a,
    {
        update_fn(database_handle, index, qualityflags, eventtime, bit0, bit1, is_double_bit);
    }
      ....
}
impl dnp3::outstation::ControlSupport<control::Group12Var1> for Dnp3simulatorControlHandler {

fn operate(
        &mut self,
        control: Group12Var1,
        index: u16,
        transmission_type: OperateType,
        database_handle: &mut DatabaseHandle,
    ) -> CommandStatus {
        println!("Performing Operate");
        //let signal_map = Arc::clone(&self.arc_signals_map);
        let state_map = Arc::clone(&self.arc_state_map);
       
        let control_alias = format!("Control_{}", index.clone());
        match self.arc_signals_map.get(&control_alias) {
            Some(signal) => {
                match &**signal {
                    Signal::Control(control_data) => {
                        if control.code.op_type == control_data.op_type && transmission_type == control_data.transmission_type {
                            if control.code.tcc == TripCloseCode::Trip || control.code.tcc == TripCloseCode::Close {
                                match state_map.resolve_tcc_to_state(&control_data.statetable, control.code.tcc) {
                                    Ok(state) => {
                                        let mut state_str = state.to_string();  //build the possible state_str
                                        match state_str.as_str() {
                                           ... some implementation of state_str
                                        }

                                        match &*control_data.feedback {
                                            Signal::Digital(digital) => {
                                                match state_map.get_dig_val(&digital.statetable, &state_str, digital.digtype) {
                                                    Ok((bit0, bit1, is_double_bit)) => {
                                                        let qualityflags: Flags = Flags::ONLINE;
                                                        let eventtime: Time = get_current_time();
                                                                                        
                                                        tokio::spawn(async move {
                                                            self.async_update_feedback(
                                                                database_handle,
                                                                digital.index.clone(),
                                                                qualityflags.clone(),
                                                                eventtime.clone(),
                                                                bit0.clone(),
                                                                bit1.clone(),
                                                                is_double_bit.clone(),
                                                                |db, idx, flags, time, b0, b1, dbl_bit| {
                                                                    db.transaction(|db| {
                                                                        db.update(
                                                                            idx,
                                                                            &BinaryInput::new(b0, flags, time),
                                                                            UpdateOptions::detect_event(),
                                                                        );
                                                                        
                                                                        if dbl_bit {
                                                                            db.update(
                                                                                idx + 1,
                                                                                &BinaryInput::new(b1, flags, time),
                                                                                UpdateOptions::detect_event(),
                                                                            );
                                                                        }
                                                                    });
                                                                },
                                                            )
                                                            .await;
                                                        });

                                                        return CommandStatus::Success
                                                    },
                                                    _=> return CommandStatus::NotSupported,
                                                } 
                                            },
                                            _ => return CommandStatus::NotSupported,
                                        } 
                                    },
                                    _ => return CommandStatus::NotSupported,
                                }    
                            }   
                        }   
                    },
                    _ => return CommandStatus::NotSupported,
                }
            },
            None => return CommandStatus::NotSupported,
        };

        CommandStatus::NotSupported
        
    }
   .....
}

"fn operate" is obvious a trait from the dnp3 library, which I cannot changed its signature
i need "self" to lived longer than 'operate' fn body, so how should one fix this?


error[E0521]: borrowed data escapes outside of method
   --> src\dnp3simulator.rs:263:57
    |
229 |           &mut self,
    |           ---------
    |           |
    |           `self` is a reference that is only valid in the method body
    |           let's call the lifetime of this reference `'1`
...
263 | /                                                         tokio::spawn(async move {
264 | |                                                             self.async_update_feedback(
265 | |                                                                 database_handle,
266 | |                                                                 digital.index.clone(),
...   |
290 | |                                                             .await;
291 | |                                                         });
    | |                                                          ^
    | |                                                          |
    | |__________________________________________________________`self` escapes the method body here
    |                                                            argument requires that `'1` must outlive `'static`

The short answer is: You can't. &mut self is an exclusive reference, meaning that no other place in the code is allowed to have access to the object being referenced.

1 Like

ok thanks

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.