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`