Windows crate (Win32 API) - Type conversion

Good morning to everyone,
I'm trying to use Windows crate in order to call win32 api.
Converting between data types is a nightmare for me.
For example, I would use use the std::ptr::copy_nonoverlapping() function
to copy a Rust string into a buffer returned by the function (windows crate api) MapViewOfFile.

This is the p_buf (MapViewOfFile return) type:

let p_buf = MapViewOfFile(file_handle.unwrap(), FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);

where p_buf is a:

pub struct MEMORY_MAPPED_VIEW_ADDRESS {
    pub Value: *mut c_void,
}

This is the string:

let str_data = String::from("Hello world!");

This is the function I need to call:

pub const unsafe fn std::ptr::copy_nonoverlapping<T>(
    src: *const T,
    dst: *mut T,
    count: usize
)

Unfortunately I was not able to convert the string pointer (&[u8]) and the MEMORY_MAPPED_VIEW_ADDRESS (or its c_void Value) into a same type T suitable to be passed as parameters of the copy_nonverlapping function as, respectively, *const T and * mut T.

I thank in advance everyone who will help me get out of the nightmare of raw pointer conversions.

use str_data.as_ptr() to get a pointer to the underlying utf8 bytes, i.e. *const u8

for the MEMORY_MAPPED_VIEW_ADDRESS, just cast the pointer using as keyword, or the built in ptr::cast() method.

something like:

std::ptr::copy_nonoverlapping(str_data().as_ptr(), p_buf.Value.cast(), str_data.len());
1 Like

Thank you very much.
It works like a charm.
I didn't know .cast() at all.
I will study this method thoroughly.
Thanks again

it's one of a family of APIs of the ptr primitive type. they are just convenient wrappers for the built in as keyword, but they make pointer casting terser to write and easier for auto completion. for example:

fn example(p: *mut i32) {
    // the following lines are doing the same thing/
    let p1 = p as *const u8;
    let p2 = p.cast::<u8>();

    // so does these
    let p3 = p as *const i32;
    let p4 = p.cast_const();

    // and these
    let p5 = p3 as *mut i32;
    let p6 = p3.cast_mut();

    // with type inference
    let p7: *mut u8 = p as _;
    let p8: *mut u8 = p.cast();

    // chaining
    let p9 = p as *const i32 as *const u8;
    let p10 = p.cast_const().cast::<u8>();

    // note, `as` keyword can cast mutability and pointee type in single step
    // but you need to chain multiple  `cast_*()` family methods to achieve the same result
    let p11 = p as *const u8;
    let p12 = p.cast_const().cast::<u8>();
}

personally I prefer to use the cast_* methods, and I use as only for integer primitives, but it's just a matter of taste really.

Very, very interesting.

Would you kindly explain to me how make the same conversions using the "as" keyword:

p_buf.Value.cast() => from *mut c_void to *const _
str_data.as_ptr()  => from &String to *mut _

Is it possible to convert *const pointers to *mut?

whoops, I think I made a mistake in my previous post. I switched the parameter order mistakenly. it should be the other way around. I made an edit to the example code there.

if I understand your requirement correctly, the rust str is the src, and the MEMORY_MAPPED_VIEW_ADDRESS returned by windows is the dst, right?

so this is how the type inference works:

first, str_data has type String, and String implemented Deref<Target = str>, so you can call str::as_ptr() on it, which returns type *const u8;

note at this step the method str::as_ptr() is not generic and no type inference is done done, but Deref coercion does play a role in method resolution.

then MEMORY_MAPPED_VIEW_ADDRESS.Value has type *mut c_void, and the <*mut T>::cast() method will return *mut U for some type U. this type is to be inferred.

finally, the signature for copy_nonoverlapping() has type for <T> fn (*const T, *mut T, usize), when you plugin the arguments type, you get the following equations:

src: *const T == *const u8;
dst: *mut T == *mut U;

the compiler then can solve this constraints and infers that T is u8, and U is u8, and generate the correct code for this function call.

using the as keyword, the syntax should be similar to:

// every typse explicitly specified:
copy_nonoverlapping::<u8>(str_data.as_ptr(), p_buf.Value as *mut u8, str_data.len());
// inferred
copy_nonoverlapping(str_data.as_ptr(), p_buf.Value as *mut _, str_data.len());
// also inferre
copy_nonoverlapping(str_data.as_ptr(), p_buf.Value as _, str_data.len());

rust type inference is like solving equations, you left some holes (i.e. unspecified types) and the compiler tries to fill them based on the constraints it has at hand, and if it can't find a solution or there's multiple potential solutions, you'll get an error.

by the way, the second conversion is not a cast, it a regular function. unlike C/C++, str (or String) are not pointer to an array of characters, they are distinct types, you cannot "cast" a str or String to a pointer. str is an unsized type, so most of time you only use a reference, i.e. &str, and &String can be coerced to &str so you can call methods defined for &str on a String variable.

Thank you so much for giving me some of your time.
I really appreciated your answers. :wave:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.