Create interface to C function pointers in Rust

2020-02-08 07:50发布

I might not have described my question title properly, please edit it if needed.

I'm trying to crate a Rust interface to LXC library, which is written in C.

I have successfully called simple functions like lxc_get_version or lxc_container_new but I cannot get access to functions described in struct lxc_container block.

Here is an part of my code:

#[link(name = "lxc")]
extern {
    // LXC part
    fn lxc_get_version() -> *const c_char;
    fn lxc_container_new(name: *const c_char, configpath: *const c_char) -> LxcContainer;

    // LXC container parts
    fn is_defined(container: &LxcContainer) -> bool; 
}

And here is an error:

note: test.o: In function `LxcContainer::is_defined::heb2f16a250ac7940Vba':
test.0.rs:(.text._ZN12LxcContainer10is_defined20heb2f16a250ac7940VbaE+0x3e): undefined reference to `is_defined'

EDIT: I have managed that functions inside C structs is called function pointers. I've tried to google something like "Rust C function pointer", but without luck.

标签: c rust
1条回答
Fickle 薄情
2楼-- · 2020-02-08 08:27

When you see something like this (in C):

struct S {
    void (*f)(int, long)
}

it means that struct S contains a field called f which is a pointer to a function. It does not mean that the library itself exposes a function called f. For example, this is valid:

void some_function_1(int x, long y) { ... }

void some_function_2(int a, long b) { ... }

int main() {
    struct S s1; s1.f = some_function_1;
    struct S s2; s2.f = some_function_2;
}

Here struct instance s1 contains a pointer to some_function_1, and s2 contains a pointer to some_function_2.

When you're writing FFI binding in Rust for some C library, you usually define Rust counterparts for C structures. Some tools like rust-bindgen can even do this automatically. In your case you will have to write something like this:

#[repr(C)]
struct LxcContainer {
    name: *mut c_char,
    configfile: *mut c_char,
    // ...
    numthreads: c_int,
    // ...
    is_defined_f: extern fn(c: *mut LxcContainer) -> bool,
    state_f: extern fn(c: *mut LxcContainer) -> *const c_char,
    // ...
}

That is, weird-looking C function pointer types correspond to extern fn function pointer types in Rust. You could also write extern "C" fn(...) -> ..., but "C" qualifier is default so it is not required.

You will have to write something like this to call these functions:

impl LxcContainer {
    fn is_defined_f(&mut self) -> bool {
        unsafe {
            (self.is_defined_f)(self as *mut LxcContainer)
        }
    }
}

You need to cast a reference to a raw pointer and you also need to wrap self.is_defined_f in parentheses in order to disambiguate between method call and field access.

You can find more on FFI in Rust here. Function pointers are explained very briefly there, though.

查看更多
登录 后发表回答