What is the best way to store a pointer as a value of another pointer?
I have a variable ptr that is of type *mut u8. How do I store the address that the ptr points to as the value of another pointer t that is also of type *mut u8.
I am trying to do something like
*t = ptr;
I get expected u8, found *-ptr error. I understand the address ptr will be 64 bits. I want to fill up 64 bits starting from address t.
I have a variable ptr that is of type *mut u8. How do I store the address that the ptr points to as the value of another pointer t that is also of type *mut u8
Assign one pointer to another:
use std::ptr;
fn main() {
let ptr: *mut u8 = ptr::null_mut();
let t: *mut u8 = ptr;
}
ptr is a pointer and the address it points to is NULL. This value is now stored in the pointer t of the same type as ptr: t points to the address NULL.
+-----+ +-----+
| | | |
| ptr | | t |
| | | |
+--+--+ +--+--+
| |
| |
+---->NULL<----+
If you wanted to have t be a pointer to the address of another pointer, you would need to take a reference to ptr. The types also could not be the same:
use std::ptr;
fn main() {
let ptr: *mut u8 = ptr::null_mut();
let t: *const *mut u8 = &ptr;
}
+-----+ +-----+
| | | |
| t +------> ptr +----->NULL
| | | |
+-----+ +-----+
I am looking for a way to write the address that the ptr points to to a specific location so that I can get the address even when I don't have t
Raw pointers have no compiler-enforced lifetimes associated with them. If you want to keep the address of something after the value has disappeared, that's an ideal case for them — you don't have to do anything:
use std::ptr;
fn do_not_dereference_this_result() -> *const u8 {
let val: u8 = 127;
let ptr: *const u8 = &val;
ptr
}
fn main() {
println!("{:p}", do_not_dereference_this_result())
}
In rarer cases, you might want to store the address in a usize (a pointer-sized integer value):
use std::ptr;
fn do_not_dereference_this_result() -> usize {
let val: u8 = 127;
let ptr: *const u8 = &val;
ptr as usize
}
fn main() {
println!("{:x}", do_not_dereference_this_result())
}
It really sounds like you are confused by how pointers work, which is a pretty good sign that you are going to shoot yourself in the foot if you use them. I'd strongly encourage you to solely use references in any important code until your understanding of pointers has increased.
You generally don't want to do that, but I'll trust you know what you're doing.
The key here is to understand a few things:
Box<T> is roughly equivalent to a *T allocated on the heap, you can use Box::into_raw to convert it into a *T.
If you do this, you're effectively leaking the heap allocated Box<T>, because Rust no longer knows where it is, or tracks it. You must manually convert it back into a droppable object at some point, for example using Box::from_raw.
You must Box::new(...) a value to ensure it is put on the heap, otherwise your raw pointer will point into the stack, which will eventually become invalid.
Mutable aliasing (which means two &mut T pointing to the same data) causes undefined behavior. It is extremely important to understand that undefined behavior is not triggered by concurrent writes to mutable aliases... it is triggered by mutable aliases existing at the same time, in any scope.
...but, if you really want to, you'd do it like this:
let foo_ref = Box::into_raw(Box::new(10));
let foo_ref_ref = Box::into_raw(Box::new(foo_ref));
// Modify via raw pointer
unsafe {
**(foo_ref_ref as *const *mut i32) = 100;
}
// Read via raw pointer
unsafe {
println!("{:?}", **foo_ref_ref);
}
// Resolve leaked memory
unsafe {
Box::from_raw(foo_ref_ref);
Box::from_raw(foo_ref);
}
Related
How can I convert a mutable u8 pointer to a mutable reference of another type?
let ptr: *mut u8;
let reference: &mut SomeType = ?; // What should i do here?
I have found a sort-of viable solution, but I wonder if there is a better way:
let reference = unsafe { &mut *(ptr as *mut SomeType) };
You have already found an acceptable method. A slightly preferable one is to use pointer::cast instead of as, because that explicitly specifies that you are trying to change the type of the referent and not any of the many other things as can do.
let ptr = ptr.cast::<SomeType>();
let reference = unsafe { &mut *ptr };
Do not use std::mem::transmute for this. Transmuting should always be the last resort in any circumstance (the nomicon and function documentation say so!), because it reinterprets the bytes regardless of what they are — in this case it'll convert any pointer-sized value, such as a reference to the pointer. By sticking with cast and &*, we catch more possible type errors. (Clippy even has a default lint against using transmute here.)
You could use std::mem::transmute instead:
use std::mem::transmute;
#[repr(transparent)]
struct SomeStruct(u8);
fn main() {
let a = &mut 10u8;
let ptr = a as *mut u8;
let reference: &mut SomeStruct = unsafe { transmute(ptr) };
}
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 *)?
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
This code:
let mut a2 = 99;
let b: *mut i32 = &mut a2;
*b = 11; // does not compile , even after unsafe {*b}
Generates the error:
error[E0133]: dereference of raw pointer requires unsafe function or block
--> src/main.rs:4:5
|
4 | *b = 11;
| ^^^^^^^ dereference of raw pointer
But this code works:
let mut a2 = 99
let b = &mut a2;
*b = 11;
What is the difference between the two?
What is the difference between the two?
One is a raw pointer (*mut _) and the other is a reference (&mut _). As the book says:
the compiler guarantees that references will never be dangling
Additionally, a reference will never be NULL. It is always safe to dereference a reference. It is not always safe to dereference a raw pointer as the compiler cannot guarantee either of those. Thus, you need an unsafe block:
unsafe { *b = 11; }
See also:
Understanding Pointer Types in Rust
References and Borrowing
Unsafe Rust
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.