I have the following scenario:
Cargo.toml
:
# …
[build-dependencies]
cc = "1.0.73"
bindgen = "0.59.2"
build.rs
:
fn main() {
bindgen::Builder::default()
.header("src/c_code.c")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.unwrap()
.write_to_file(
std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap())
.join("c_code.rs"),
)
.unwrap();
println!("cargo:rerun-if-changed=src/c_code.c");
cc::Build::new().file("src/c_code.c").compile("c_code.a");
}
src/c_code.c
:
#include<stddef.h>
void return_size_t_through_ptr(size_t *ptr) {
*ptr = 17;
}
src/main.rs
:
#![feature(c_size_t)]
use std::ffi::c_size_t;
use std::mem::MaybeUninit;
mod bindings {
#![allow(warnings)]
include!(concat!(env!("OUT_DIR"), "/c_code.rs"));
}
fn main() {
let mut x = MaybeUninit::<c_size_t>::uninit();
unsafe {
bindings::return_size_t_through_ptr(x.as_mut_ptr());
}
let x = unsafe { x.assume_init() };
assert_eq!(x, 17);
}
And I get the following error.
Error:
error[E0308]: mismatched types
--> src/main.rs:14:45
|
14 | bindings::return_size_t_through_ptr(x.as_mut_ptr());
| ^^^^^^^^^^^^^^ expected `u64`, found `usize`
|
= note: expected raw pointer `*mut u64`
found raw pointer `*mut usize`
For more information about this error, try `rustc --explain E0308`.
I see several possibilities to solve this:
Solution 1: Use size_t_is_usize
option
bindgen::Builder::default()
.header("src/c_code.c")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
+ .size_t_is_usize(true)
.generate()
.unwrap()
This works now, but will this work in future? (edit: and on all platforms?)
Solution 2: Cast to *mut _
fn main() {
let mut x = MaybeUninit::<c_size_t>::uninit();
unsafe {
- bindings::return_size_t_through_ptr(x.as_mut_ptr());
+ bindings::return_size_t_through_ptr(x.as_mut_ptr() as *mut _);
}
let x = unsafe { x.assume_init() };
Doesn't feel safe if the API changes.
Solution 3: Cast to *mut bindings::size_t
fn main() {
let mut x = MaybeUninit::<c_size_t>::uninit();
unsafe {
- bindings::return_size_t_through_ptr(x.as_mut_ptr());
+ bindings::return_size_t_through_ptr(x.as_mut_ptr() as *mut bindings::size_t);
}
let x = unsafe { x.assume_init() };
But why do we have c_size_t
at all? Maybe better not use it at all while it's still unstable?
Solution 4: Don't use std::ffi::c_size_t
but take it from bindgen
output
-#![feature(c_size_t)]
-
-use std::ffi::c_size_t;
use std::mem::MaybeUninit;
mod bindings {
#![allow(warnings)]
include!(concat!(env!("OUT_DIR"), "/c_code.rs"));
}
+#[allow(non_camel_case_types)]
+type c_size_t = bindings::size_t;
+
fn main() {
let mut x = MaybeUninit::<c_size_t>::uninit();
unsafe {
bindings::return_size_t_through_ptr(x.as_mut_ptr());
}
let x = unsafe { x.assume_init() };
It all feels a bit awkward. What would you do?