I've discovered some unexpected behavior when capturing structs with Copy
members & wondering if this is expected (and maybe thinking either the docs could be improved or this might be marked as an inconsistency to be fixed in future editions).
The basic idea is that I've got a repr(C) struct containing some arbitrary data & a callback for doing some FFI callbacks and another callback for deallocating the data when we're done with it. I want to be able to save this in a Box<dyn Fn(..)>
trait object and call it later, but to my dismay, the Drop() operation was being called on the object as soon as the constructing block returned, even though I thought the closure would be capturing via move, due to the use of the move
keyword.
I think what is happening is that despite specifically requesting move capture, because the member types are Copy
, only those fields are being captured, not the whole object.
I've found the workaround of let f = &f;
which causes the whole object to be captured, but I'm wondering if there's any way to annotate the struct such that this could be avoided.
Any input would be appreciated, Below is a simplified example
use std::ffi::c_void;
#[repr("C")]
struct ContainsCopyableMembers {
pub foreign_data: *mut c_void,
pub destructor: Option<unsafe extern "C" fn(*mut c_void)>
}
impl Drop for ContainsCopyableMembers {
fn drop(&mut self) {
println!("Calling destructor....");
if let Some(destructor) = self.destructor {
unsafe {
(destructor)(self.foreign_data);
}
}
}
}
pub struct DynHolder {
callable: Box<dyn Fn() -> u32>,
}
pub extern "C" fn delete_foreign_data(data: *mut c_void) {
unsafe{Box::from_raw(data)}; // causes drop
}
fn main() {
let fake_foreign_data : Box<u32> = Box::new(64);
let cbh;
{
let cb = ContainsCopyableMembers{
foreign_data : Box::into_raw(fake_foreign_data) as *mut c_void,
destructor: Some(delete_foreign_data)
};
cbh = DynHolder{callable: Box::new(move || {
// let cb = &cb; //Uncomment this line to get proper capture
unsafe{*(cb.foreign_data as *const u32)}
})};
}
println!("{}", (cbh.callable)());
}
Output:
Calling destructor....
4176920654