Stream: t-lang/wg-unsafe-code-guidelines

Topic: piecewise initialization of uninitialized structs


Jake Goulding (May 19 2019 at 11:45, on Zulip):

Am I correct in understanding that the reason that we cannot have piecewise initialization is that getting access to a field requires creating a reference to the field, but we can't uphold the requirements of references because the referred-to value is invalid for the type?

And is it correct that this is one of the motivations behind the &raw syntax?

/cc @RalfJ

rkruppe (May 19 2019 at 11:54, on Zulip):

Yes. More precisely "getting access to a field" in the sense of creating a raw pointer to it. Directly writing through a raw pointer field projection like (*p).field = value; should be OK AFAIK (though this has not been discussed as extensively).

Jake Goulding (May 19 2019 at 12:06, on Zulip):

but there's no way to safely get a raw pointer to the field, if I wanted to pass it to a C function, right?

Jake Goulding (May 19 2019 at 12:07, on Zulip):

(this is generally in the context of this comment)

Jake Goulding (May 19 2019 at 12:09, on Zulip):

It's a common Windows API technique to have an uninitialized struct where you've set a length or version field before passing it to a function. If it's OK to use a field projection, then that should be fine.

RalfJ (May 19 2019 at 12:22, on Zulip):

Yes. More precisely "getting access to a field" in the sense of creating a raw pointer to it. Directly writing through a raw pointer field projection like (*p).field = value; should be OK AFAIK (though this has not been discussed as extensively).

the problem with this is that it drops the old value in the field

RalfJ (May 19 2019 at 12:23, on Zulip):

so if the type has drop but the field was uninitialized... we got UB, and (unlike the more theoretical issue around creating references to uninitialized fields) this will likely lead to real crashes

RalfJ (May 19 2019 at 12:23, on Zulip):

It's a common Windows API technique to have an uninitialized struct where you've set a length or version field before passing it to a function. If it's OK to use a field projection, then that should be fine.

given that these are likely fields of dropless type, writing to a field projection should work

RalfJ (May 19 2019 at 12:25, on Zulip):

but it is a dangerous pattern to use, in case someone reads it and then uses it for their type-with-drop...

RalfJ (May 19 2019 at 12:26, on Zulip):

@Jake Goulding ^

Jake Goulding (May 19 2019 at 12:30, on Zulip):

yup, makes sense, thanks!

Jake Goulding (May 19 2019 at 12:30, on Zulip):

Mostly collecting knowledge to see if I need to go back and update SO answers

RalfJ (May 19 2019 at 12:46, on Zulip):

thanks for that :)

Jake Goulding (May 19 2019 at 15:29, on Zulip):
extern "C" {
    fn some_lib_func(x: *mut some_lib_struct_t);
}

unsafe {
    let mut x: some_lib_struct_t = std::mem::uninitialized();
    some_lib_func(&mut x);
}

So, this is UB, right? Since we create a &mut T before making it a *mut T?

rkruppe (May 19 2019 at 17:06, on Zulip):

Yeah but that's not "piecewise", for this the accepted-for-stabilization subset of the MaybeUninit API (soon to be stable in a nightly near you) is sufficient

Jake Goulding (May 19 2019 at 20:26, on Zulip):

ah, good point.

Last update: Nov 19 2019 at 17:50UTC