Reputation: 8092
I want to create a C FFI API for my crate, but it's not clear how safe it is to cast pointers. Pseudocode:
#[no_mangle]
extern "C" fn f(...) -> *mut c_void {
let t: Box<T> = ...;
let p = Box::into_raw(t);
p as *mut c_void
}
This works as expected, but how safe is it? In C or C++, there is special void *
pointer and the C++ standard declares that it is safe to cast to it. Potentially, sizeof(void *)
may be not equal sizeof(T *)
, but there is a guarantee that sizeof(void *)
>= sizeof(T *)
.
What about Rust? Is there any guarantee about the std::mem::size_of
of a pointer or safe casting between pointers? Or do all pointers have equal size by implementation, equal to usize
?
By "universal", I mean that you can convert X *
without losing anything. I do not care about type information; I care about different sizes of pointers to different things, like near
/far
pointers in the 16-bit days.
4.10 says
The result of converting a "pointer to cv T" to a "pointer to cv void" points to the start of the storage location where the object of type T resides,
It is impossible that sizeof(void *) < sizeof(T *)
, because then it is impossible to have real address of storage location.
Upvotes: 8
Views: 4298
Reputation: 430663
No.
Rust's raw pointers (and references) currently come in two flavors:
use std::mem;
fn main() {
println!("{}", mem::size_of::<*const u8>()); // 8
println!("{}", mem::size_of::<*const [u8]>()); // 16
}
There's no type that allows storing both; even the Big Hammer of mem::transmute
won't work:
use std::mem;
unsafe fn example(mut thin: *const u8, mut fat: *const [u8]) {
fat = mem::transmute(thin);
thin = mem::transmute(fat);
}
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> src/main.rs:4:11
|
4 | fat = mem::transmute(thin);
| ^^^^^^^^^^^^^^
|
= note: source type: `*const u8` (64 bits)
= note: target type: `*const [u8]` (128 bits)
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> src/main.rs:5:12
|
5 | thin = mem::transmute(fat);
| ^^^^^^^^^^^^^^
|
= note: source type: `*const [u8]` (128 bits)
= note: target type: `*const u8` (64 bits)
Since the layout of fat pointers is a Rust-specific concept, they should never be accessed via FFI. This means that only thin pointers should be used, all of which have a uniform known size.
For those types, you should use an opaque pointer to provide better type safety. You could also use *const ()
or *const libc::c_void
.
See also:
In C or C++, there is special
void *
pointer and the C++ standard declares that it is safe to cast to it.
This isn't always true:
Upvotes: 10