Rust closure as callback for C bindings receiving garbage value in captured variable

房东的猫 提交于 2019-12-12 02:44:42

问题


I'm writing Rust wrappers for C bindings so that they look more Rusty. One such C function is this:

void mosquitto_connect_callback_set(
    struct mosquitto * mosq,
    void (*on_connect)(struct mosquitto *, void *, int)
)

I'm using the below technique to pass a Rust closure as the user data to above binding (void* in the callback) so that the Rust closure will be called when the C callback is invoked.

//  Registered callback is called when the broker sends a CONNACK message in response
// to a connection. Will be called even incase of failure. All your sub/pub stuff
// should ideally be done in this callback when connection is successful

pub fn onconnect_callback<F>(&self, callback: F)
    where F: Fn(i32)
{
    // Convert the rust closure into void* to be used as user_data. This will
    // be passed to callback automatically by the library
    let cb = &callback as *const _ as *mut libc::c_void;

    unsafe {
        // Set our closure as user data
        bindings::mosquitto_user_data_set(self.mosquitto, cb);
        // Register callback
        bindings::mosquitto_connect_callback_set(self.mosquitto, Some(onconnect_wrapper::<F>));
    }

    // Registered callback. user data is our closure
    unsafe extern "C" fn onconnect_wrapper<F>(mqtt: *mut bindings::Struct_mosquitto,
                                              closure: *mut libc::c_void,
                                              val: libc::c_int)
        where F: Fn(i32)
    {
        let closure = closure as *mut F;
        println!("rc = {:?}", val as i32);
        (*closure)(val as i32);

    }
}

But the problem is that user data is set using a function instead of directly passing it to the callback set function

// Set our closure as user data
bindings::mosquitto_user_data_set(self.mosquitto, cb);

I think the callback: F closure passed to onconnect_callback might get destroyed by the time the actual C callback is invoked. This might be the reason I'm getting garbage values when capturing a variable.

let i = 100;

client.onconnect_callback(|a: i32|{
    println!("i = {:?}", i);
    println!("@@@ On connect callback {}@@@", a)
    });

match client.connect("localhost"){
    Ok(_) => println!("Connection successful --> {:?}", client),
    Err(n) => panic!("Connection error = {:?}", n)
}

OUTPUT:

i = 734146560
@@@ On connect callback 0@@@

How do I fix this without passing closure as reference?

The full code

来源:https://stackoverflow.com/questions/34247879/rust-closure-as-callback-for-c-bindings-receiving-garbage-value-in-captured-vari

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!