Rust bindgen does not treat `const` in C const enough in Rust

Rust's bindgen is a wonderful tool to create Rust bindings for C. However, I think it does not treat C's const gracefully enough.

For example, in a C code, we have

const int MY_VAL = 1234;

With rust bindgen generated binding, in our rust code, we use that value, e.g.

println!("{}", MY_VAL + 1);

After using cargo build --release to generate the optimized binary (in Ubuntu 22.04.1, x86_64), we can find that the code in rust is compiled to something ugly (in AT&T syntax):

lea    MY_VAL(%rip), %rax
mov    (%rax), %eax
add    $0x1, %eax

It uses a memory access to get MY_VAL for calculating.

But for Rust's true const, the compiler can just use the value 1234 directly without any memory accesses.

In my understanding, the reason for this memory access is that, Rust's bindgen generates a static Rust variable rather than a const Rust value for the const C value, and since static value can be mutated in an unsafe way, the compiler can only generate a memory access rather than replace the value directly. And maybe the reason to generate static variable is Rust does not support extern "C" { const xxx }?

I wonder whether there is a graceful way to generate bindings for C's constant.

MY_VAL needs to have an address and linker symbol to match C’s const semantics, which is equivalent to static in Rust. In particular, this is also legal C:

const int MY_VAL;  /* Value provided in a different .o file */

(NB: I haven’t written C in a long time, so I may be misremembering)

4 Likes

Yes, that makes sense. I wonder if we could have a hint to tell bindgen that the constant could be safely transpiled to Rust's const.

If it helps, bindgen will translate #define into a const variable.

1 Like

However there is bindgen issue #2120, which I find a bit troublesome.