Why does `copy` from `slice` cause UB but not from `slice.as_ptr()`?

I was trying to fill a slightly awkward FFI structure using ptr::copy and my app crashed. When debugging, I realized that copy(slice, ...) behaves differently than copy(slice.as_ptr(), ...), which I found surprising.

Here is the code in question:

struct S {
    array: [u8; 256],

fn main() {
    let mut s = S { array: [0; 256] };
    let hello = b"hello world\0";

    unsafe {
        std::ptr::copy(hello         , s.array.as_mut_ptr() as *mut _, hello.len());        // causes UB
        //std::ptr::copy(hello.as_ptr(), s.array.as_mut_ptr() as *mut _, hello.len());      // OK

        dbg!(hello as *const _);           // [src\main.rs:15] hello          as *const _ = 0x00007ff7f2a9e3d8
        dbg!(hello.as_ptr() as *const _);  // [src\main.rs:16] hello.as_ptr() as *const _ = 0x00007ff7f2a9e3d8



  • The first copy causes UB, which is confirmed by Miri, and the output if dbg!(s) is garbled.
  • The second copy works as expected.

Why is that? Both debug lines below print the same address (e.g., 0x00007ff7f2a9e3d8), and I thought that a slice pointer can be automatically coerced to a pointer to the first element?

Type inference tripped you up. A pointer-to-array doesn't automatically become a pointer-to-element, so your types got inferred to be *const [u8; 12], which is too big. Being more explicit works under MIRI too:

std::ptr::copy(hello as *const _ as *const u8, s.array.as_mut_ptr() as *mut u8, hello.len());

In general, you should always be explicit with your types in unsafe.


The variable hello's type is &'static [u8; 12], so std::ptr::copy(hello, some_ptr, hello.len()) will copy 12 * 12 = 144 bytes. Copying 144 bytes from it indeed is UB.

1 Like

Ah yes, of course! Thanks!

In situations like this, it might be useful to be explicit in the type of the copy:


This way, you're not relying on anything about the arguments to ensure that the operation is doing what you expect, and a reader can instantly see "this is copying a certain count of u8s". (I've found this approach useful in non-unsafe code using bytemuck, where even if there's no UB, converting the wrong type will result in the wrong result.)


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.