[HELP]Cast pointer get different results in diff platforms


#1

The test code is:

use std::mem::transmute;

fn main(){
unsafe{
let mut a:*mut u16 = transmute((vec!['w' as u8, 0u8, 'h' as u8, 0u8, 'a' as u8, 0u8,'t' as u8, 0u8]).as_mut_ptr());
let mut raw = ::std::slice::from_raw_parts::<u8>(a as *mut u8, 20);
println!("{:?}", raw);
let mut raw = ::std::slice::from_raw_parts::<u8>(transmute(a), 20);
println!("{:?}", raw);

let mut b:*mut u16 = (vec!['w' as u8, 0u8, 'h' as u8, 0u8, 'a' as u8, 0u8,'t' as u8, 0u8]).as_mut_ptr() as *mut u16;
let mut raw = ::std::slice::from_raw_parts::<u8>(b as *mut u8, 20);
println!("{:?}", raw);
let mut raw = ::std::slice::from_raw_parts::<u8>(transmute(a), 20);
println!("{:?}", raw);

println!("the w is:{:?}",'w' as u8);
println!("the h is:{:?}",'h' as u8);
println!("the a is:{:?}",'a' as u8);
println!("the t is:{:?}",'t' as u8);
}
}

The output at my PC:

$ /e/rustdir/target/debug/test_cast_msvc_i686.exe
[8, 208, 3, 1, 196, 0, 95, 0, 30, 78, 157, 171, 154, 45, 0, 8, 91, 56, 44, 32]
[8, 208, 3, 1, 196, 0, 95, 0, 30, 78, 157, 171, 154, 45, 0, 8, 91, 56, 44, 32]
[196, 0, 95, 0, 196, 0, 95, 0, 69, 8, 0, 77, 154, 45, 0, 0, 196, 0, 95, 0]
[8, 208, 3, 1, 196, 0, 95, 0, 30, 78, 157, 171, 154, 45, 0, 8, 91, 56, 44, 32]
the w is119

My PC info:
Windows 64bit

rustup info:

$ rustup show
Default host: x86_64-pc-windows-gnu

installed toolchains
--------------------

nightly-i686-pc-windows-gnu
nightly-i686-pc-windows-msvc (default)

active toolchain
----------------

nightly-i686-pc-windows-msvc (default)
rustc 1.22.0-nightly (dead08cb3 2017-09-08)

The out put at https://play.rust-lang.org/?version=nightly&mode=debug:

[16, 192, 248, 108, 156, 85, 0, 0, 80, 64, 226, 40, 42, 127, 0, 0, 0, 0, 0, 0]
[16, 192, 248, 108, 156, 85, 0, 0, 80, 64, 226, 40, 42, 127, 0, 0, 0, 0, 0, 0]
[119, 0, 104, 0, 97, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[16, 192, 248, 108, 156, 85, 0, 0, 80, 64, 226, 40, 42, 127, 0, 0, 119, 0, 104, 0]
the w is:119

It seem that playground’s result: [119, 0, 104, 0, 97, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] is the correct result.


#2

I didn’t dive into the details here (yet), but this code is broken because the Vec is dead right after you transmute. At that point you’re pointing at garbage and anything goes, technically speaking.


#3

In addition to what @vitalyd said about the Vec being dropped the moment you try and transmute it, I’d like to mention that using transmute in this way is UB and going to lead you to a world of hurt.

A Vec is essentially a tuple of a raw pointer to some memory, an integer indicating the number of elements, and an integer indicating the amount of memory that was allocated (capacity). When you transmute a u8 vector to a u16 vector the length and capacity will stay the same, but the vector will think it now has twice the amount of memory to work with (size_of::<u16>() * len), so the moment you do anything which may result in resizing the underlying buffer, or if you append to the end you’ll segfault and have .

As a rule of thumb, unless you know exactly what you are doing and what could go wrong, transmute isn’t the tool you are looking for. And even then you should strongly reconsider looking for a better way. It’s arguably more dangerous than casts are in C, because most of Rust assumes you have 100% valid and correctly constructed data, and the moment you pull out transmute all those assumptions are broken.

TL; DR: don’t use transmute. It’s the nuclear option that way too many beginners (and even some experts) reach for, and then we end up with broken code and much sadness…


#4

So keeping the vec alive prints the expected bytes for the start (@Michael-F-Bryan’s warning/advice still stands) , and then whatever garbage is picked up by the fact the slice is longer than the “valid” bytes in the original vec. If you vec::reserve() sufficient capacity, then you’ll just get 0s since the vec zeros internal storage.


#5

The Vec doesn’t zero anything outside its length (as defined by .len()), so reading past the length but inside capacity by any means is not advised – that memory is uninitialized.


#6

Indeed. There must be something funky going on with the underlying allocator because adding any non-zero Vec::reserve() call zeros out the tail of the slice; without it you get random garbage.

https://play.rust-lang.org/?gist=767c782dbb1569ed55e5e3163aab8d65&version=nightly


#7

Thanks, to everyone!

But I don’t understand why the vec’s memmory is reclaimed.
Within this line, there must be a temporary variate hold the data of
vec![‘w’ as u8, 0u8, ‘h’ as u8, 0u8, ‘a’ as u8, 0u8,‘t’ as u8, 0u8]
and the data is created before the pointer. So it should live longer than the pointer.


#8

There is no borrow check for pointers. The pointer is independent from the temporary Vec.
To keep the data alive (other than keeping the structure) you would need to change to a Box and then Box::into_raw