How do I over-align a variable in Rust? - pointers

In my code, I have a type that is aligned to 1 byte, and a function that requires a type that is aligned to 8 bytes. The following hypothetical code shows this usage:
fn use_bar(bar: &mut [u64; 25]) {
unimplemented!()
}
fn main() {
let mut foo: [u8; 200] = get_foo();
unsafe {
// Option 1
use_bar(mem::transmute::<&mut [u8; 200], &mut [u64; 25]>::(&mut foo));
// Option 2
use_bar(&mut *(&mut foo as *mut [u8; 200] as *mut [u64; 25]));
}
}
Unfortunately, this doesn't necessarily work. If you ask clippy about the first option, it will tell you that transmuting references is a bad thing to do. Option 2 may work, however, it will then tell you that the alignment requirements for [u64; 25] are more strict (8 byte alignment) than for [u8; 200] (1 byte alignment) so this may cause undefined behaviour.
Since I don't control the type returned by get_foo(), is there any way I can force foo to be 8 byte aligned? (other than wrapping it in a struct that is properly aligned)

Use align_to to get an aligned slice.
To get data aligned in the first place, you can use a wrapper with #[repr(align(x))]:
#[repr(align(8))]
struct Wrapper([u8; 200]);

Related

Does casting pointers in Rust have the same behavior as reinterpret_cast in C++?

I have this struct defined in my code:
#[repr(align(4))]
struct MetaDataDefn {
cncVersion: i32,
toDriverBufferLength: i32,
toClientsBufferLength: i32,
counterMetadataBufferLength: i32,
counterValuesBuferLength: i32,
clientLivenessTimeout: i64,
startTimestamp: i64,
pid: i64
}
I have a function that takes a raw pointer to a chunk of memory, where the first bytes correspond to a struct with the same layout.
I thought that if I cast the u8 pointer to a struct pointer, I'd get the same result as if I did a reinterpret_cast in C++. However, I think that's not the case, and I'm a bit confused about what's going on here. This is the body of the function (the pointer that the function receives is cncFilePtr):
let metadata = unsafe { cncFilePtr as *mut MetaDataDefn };
// This works
let cncVersion = unsafe { (*(cncFilePtr as *mut i32)) };
println!("CNC Version: {}", cncVersion);
//This prints a different number than the previous code
println!("CNC version (other way): {}", unsafe { (*metadata).cncVersion });
As you can see, casting the first 4 bytes to a i32 and then printing the result gives a different result than casting the whole thing to MetaDataDefn and accessing the first member, which is of type i32 (my understanding is that both approaches should give the same result)
My question is: why it's not the same result? is casting pointers in Rust not the same as reinterpret_cast in C++ (I come from a C++ background)?
Normally, Rust makes no guarantees about the way that a struct is represented in memory. It can reorder fields to make them pack more tightly, and could theoretically even optimise the field order based on how your application actually accesses them.
You can fix the order, to behave like C, by adding the #[repr(C)] attribute:
#[repr(C)]
#[repr(align(4))]
struct MetaDataDefn { ... }
With that, both pointers will give the same result because this guarantees that cncVersion appears first.

Is there a universal Rust pointer type that can store any other kind of pointer, an analog of C's void *?

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.
No.
Rust's raw pointers (and references) currently come in two flavors:
thin (one native-sized integer in size)
fat (two native-sized integers in size)
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:
What's the Rust idiom to define a field pointing to a C opaque pointer?
Why can comparing two seemingly equal pointers with == return false?
How do I pass a closure through raw pointers as an argument to a C function?
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:
Why can't I cast a function pointer to (void *)?

How do I return a reference to a concrete type that was added to a vector of trait objects?

In this code, I take a vector, create a struct instance, and add it to the vector boxed:
trait T {}
struct X {}
impl T for X {}
fn add_inst(vec: &mut Vec<Box<T>>) -> &X {
let x = X {};
vec.push(Box::new(x));
// Ugly, unsafe hack I made
unsafe { std::mem::transmute(&**vec.last().unwrap()) }
}
Obviously, it uses mem::transmute, which makes me feel it's not the right way to do this. Is this ugly hack the only way to do it?
Additionally, while this compiles in Rust 1.32, it fails in Rust 1.34:
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> src/lib.rs:10:14
|
10 | unsafe { std::mem::transmute(&**vec.last().unwrap()) }
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `&dyn T` (128 bits)
= note: target type: `&X` (64 bits)
I think that this code is safe:
fn add_inst(vec: &mut Vec<Box<dyn T>>) -> &X {
let x = X {};
let b = Box::new(x);
let ptr = &*b as *const X;
vec.push(b);
unsafe { &*ptr }
}
The trick is to save a raw pointer to *const X before converting it to a Box<dyn T>. Then you can convert it back to a reference before returning it from the function.
It is safe because a boxed value is never moved, (unless it it moved out of the Box, of course), so ptr survives the cast of b into Box<dyn T>.
Your "ugly hack" is actually completely incorrect and unsafe. You were unlucky that Rust 1.32 doesn't report the error, but thankfully Rust 1.34 does.
When you store a boxed value, you create a thin pointer. This takes up the platform-native size of an integer (e.g. 32-bit on 32-bit x86, 64-bit on 64-bit x86, etc.):
+----------+
| pointer |
| (0x1000) |
+----------+
When you store a boxed trait object, you create a fat pointer. This contains the same pointer to the data and a reference to the vtable. This pointer is two native integers in size:
+----------+----------+
| pointer | vtable |
| (0x1000) | (0xBEEF) |
+----------+----------+
By attempting to perform a transmute from the trait object to the reference, you are losing one of those pointers, but it's not defined which one. There's no guarantee which comes first: the data pointer or the vtable.
One solution would use std::raw::TraitObject, but this is unstable because the layout of fat pointers is still up in the air.
The solution I would recommend, which requires no unsafe code, is to use Any:
use std::any::Any;
trait T: Any {}
struct X {}
impl T for X {}
fn add_inst(vec: &mut Vec<Box<dyn T>>) -> &X {
let x = X {};
vec.push(Box::new(x));
let l = vec.last().unwrap();
Any::downcast_ref(l).unwrap()
}
If you couldn't / don't want to use Any, I've been told that casting a trait object pointer to a pointer to a concrete type will only keep the data pointer. Unfortunately, I cannot find an official reference for this, which means I can't fully vouch for this code, although it empirically works:
fn add_inst(vec: &mut Vec<Box<dyn T>>) -> &X {
let x = X {};
vec.push(Box::new(x));
let last: &dyn T = &**vec.last().unwrap();
// I copied this code from Stack Overflow without reading
// it and it may not actually be safe.
unsafe {
let trait_obj_ptr = last as *const dyn T;
let value_ptr = trait_obj_ptr as *const X;
&*value_ptr
}
}
See also:
Why can comparing two seemingly equal pointers with == return false?
How to get a reference to a concrete type from a trait object?
Accessing the last element of a Vec or a slice

How do I make the equivalent of a C double pointer in Rust?

I started porting C code to Rust, but I'm confused about how things work in Rust. What is the equivalent of this code:
typedef struct Room {
int xPos;
int yPos;
} Room;
void main (){
Room **rooms;
rooms = malloc(sizeof(Room)*8);
}
What is the equivalent of this code
Assuming you mean "a collection of Rooms with capacity for 8":
struct Room {
x_pos: i32,
y_pos: i32,
}
fn main() {
let rooms: Vec<Room> = Vec::with_capacity(8);
}
It's exceedingly rare to call the allocator directly in Rust. Generally, you have a collection that does that for you. You also don't usually explicitly specify the item type of the collection because it can be inferred by what you put in it, but since your code doesn't use rooms at all, we have to inform the compiler.
As pointed out in the comments, you don't need a double pointer. This is the equivalent of a Room *. If you really wanted an additional level of indirection, you could add Box:
let rooms: Vec<Box<Room>> = Vec::with_capacity(8);
How do I make the equivalent of a C double pointer
One of the benefits of Rust vs C is that in C, you don't know the semantics of a foo_t **. Who should free each of the pointers? Which pointers are mutable? You can create raw pointers in Rust, but even that requires specifying mutability. This is almost never what you want:
let rooms: *mut *mut Room;
In certain FFI cases, a C function accepts a foo_t ** because it wants to modify a passed-in pointer. In those cases, something like this is reasonable:
unsafe {
let mut room: *mut Room = std::ptr::null_mut();
let room_ptr: *mut *mut Room = &mut room;
}

How do you create a ?Sized type from a raw pointer?

Here is an example of how to transmute a Sized type from a raw pointer:
use std::mem;
#[derive(Eq, PartialEq)]
#[repr(packed)]
struct Bob {
id: u32,
age: u32,
}
unsafe fn get_type<'a, T: Sized>(p: *const u8) -> &'a T {
mem::transmute(p)
}
#[test]
fn it_works() {
let bob = Bob {
id: 22,
age: 445,
};
let bob2: &Bob = unsafe {
let ptr: *const u8 = mem::transmute(&bob);
get_type(ptr)
};
assert_eq!(&bob, bob2);
}
However, for my application I want to be able to get a ?Sized type instead of a Sized type. However, this doesn't work:
unsafe fn get_type2<'a, T: ?Sized>(p: *const u8) -> &'a T {
mem::transmute(p)
}
It fails with this error message:
error: transmute called with differently sized types: *const u8 (64 bits) to &'a T (pointer to T) [--explain E0512]
--> src/main.rs:2:9
|>
2 |> mem::transmute(p)
|> ^^^^^^^^^^^^^^
I have tried to give it a &[u8] (fat pointer) by converting it using std::slice::from_raw_parts, but it fails with pretty much the same error message.
You actually cannot for the very reason cited in the error message.
Rust references can be either pointer-sized (for Sized types) or bigger (for !Sized types). For example, if Trait is a trait, a &Trait reference is actually two fields as defined by std::raw::TraitObject.
So, in order to form a reference to an unsized type, you have to:
identify exactly what kind of unsized type it is (trait? slice? ...)
pick the right representation (std::raw::TraitObject, std::raw::Slice, ...)
and then you have to fill in the blanks (there is more than just a pointer).
So, unless you can limit your function to producing &T where T: Sized, you cannot just transmute a raw pointer to &T.

Resources