Problem with matching Option<extern "C" fn() -> i32> callback function


#1

I have a struct callback field set to:

type Handler {
    cb : Option< extern "C" fn() -> i32 >,
}

Regardless whether the Handler::cb field is initialized as None or Some(“extern “C” fn() -> i32 object”) the following method code enters invariably into the Some(f) alternative:

 pub fn handle_idling(&mut self) -> i32 {
    let mut o: Option i32> = unsafe { (*self).cb };
    let err = match o {
        Some(f) => {
            println!("We're trying the callback ...");
            unsafe { f() };
            }
        None => {
            println!("Skipping callback!");
            0
            }
        };
       err 
}

In both cases (i.e. using and not using the callback function) the execution segfaults… Help much appreciated!


#2

Can you try:

pub fn handle_idling(&mut self) -> i32 {
    if let Some(ref f) = self.cb
    { 
            println!("We're trying the callback ...");
            unsafe { *f() }
    } else {
            println!("Skipping callback!");
            0
   }
}

I’m not 100% on the * in the unsafe { *f() }. So you might try it without.


#3

When you’re asking for help, you need to be thorough and accurate. Your example code does not compile, even if I fill in the missing parts. Beyond that, there are numerous suspicious problems (such as the entirely needless unsafe blocks, and "extern "C" fn() -> i32 object" not meaning anything in context), which makes me wonder if this is even what the code looks like.

Here is a complete example that works perfectly. If there’s a problem, it’s somewhere else.

struct Handler {
    cb: Option<extern "C" fn() -> i32>,
}

impl Handler {
    pub fn handle_idling(&mut self) -> i32 {
        let o = self.cb;
        let err = match o {
            Some(f) => {
                println!("We're trying the callback ...");
                f()
            },
            None => {
                println!("Skipping callback!");
                0
            },
        };
        err 
    }
}

extern "C" fn hi_42() -> i32 {
    println!("Hi!");
    42
}

fn main() {
    (Handler { cb: Some(hi_42) }).handle_idling();
    (Handler { cb: None }).handle_idling();
}

We can’t help you without accurate information, nor without a complete description of the problem.


#4

Since Daniel gave me code I could test with it appears I didn’t need the “ref f”. I guess because a function pointer is copyable, so this should work:

pub fn handle_idling(&mut self) -> i32 {
    if let Some(f) = self.cb
    { 
            println!("We're trying the callback ...");
            f()
    } else {
            println!("Skipping callback!");
            0
   }
}

#5

The problem seems to be somewhere else… Thanks.


#6

Doesn’t solve it either for me, so the problem seems to be somewhere else… Thanks.


#7

Look for any places you have unsafe. In particular, any place you construct or transport those values using unsafe or in code that Rust doesn’t see. What you’re doing should be safe, which suggests you’re doing something inadvisable.


#8

Solved, thanks to both your help. The problem was related to the (*self).cb pointer ref. Changing it to (safe) = self.cb was the ‘trick’.

To be honest my original problem is a bit more involved (…simplified for communication reasons…) where the callback was one struct deeper, i.e.

type DeepHandler {
    cb : Option< extern "C" fn() -> i32 >,
}

type Handler {
    dh : *mut DeepHandler,
}

Hence, with self being the Handler object,

let mut o: Option i32> = unsafe { (*self.dh).cb };

which caused the problem, …for reasons still unknown to me since I have an toy example (copied from the Internet) that works. Anyway, changing Handler into

type Handler {
    dh : DeepHandler,
}

did it. Thanks again!