Is it still a UB if an uninitialized Copy value was sent into oblivion immediately after read?

Suppose we have a union which have potentially unintiailized field

We read the unintialized field and then immediately place it into another unintialized location

#[repr(C)]
union Foo {
    single: i32,
    double: (i32, i32),
}

fn main() {
    let x = Foo { single: 7 };
    let value = unsafe {
        Foo {
            double: (7, x.double.1),
        }
        .single
    };
    println!("{}", value);
}

MIRI will complain

19 |             double: (7, x.double.1),
   |                         ^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
   |

But is this a real UB?

Yes. It's a typed read, and i32's validity invariant requires that it be initialized.

If you want to do an untyped copy, that preserves bits (including uninitialized-ness) exactly, copy it using copy_nonoverlapping in std::ptr - Rust instead.

1 Like

Thanks for the reply. I always interpreted the spec as the UB must requires some kind of a usage of the value we obtained. Never really thought the obtaining action itself is defined as UB.

The x.double.1 is a read (a copy) of it, which is the use.

There are ways you can mention the place without it being a read, and which are thus not UB. For example, MIRI confirms that this is allowed:

let x = Foo { single: 7 };
unsafe {
    // Not a read, because `_` is special.
    let _ = x.double.1;
}
let value = unsafe {
    x.single
};
println!("{}", value);

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=ebda9360f92b3884a741a221d34e5903

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.