Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upUnion initialization and Drop #2514
Conversation
RalfJung
added some commits
Aug 3, 2018
petrochenkov
reviewed
Aug 3, 2018
| // We can write into uninitialized inner fields: | ||
| u.f2.1 = S(42); | ||
| let _ = &u.f2.1; // This field is initialized now. |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Aug 3, 2018
Contributor
let _ is a bit of a footgun in terms of testing initialization.
_ pattern doesn't access the right side at all, so even something like this
let x: u8;
let _ = x;successfully compiles.
You need let _y = x; or just x; to test for x being initialized.
This comment has been minimized.
This comment has been minimized.
joshtriplett
reviewed
Aug 3, 2018
| let _ = &u.f2.0; | ||
| // Equivalently, we can assign the entire union: | ||
| u = U { f2: S(42) }; |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
scottmcm
added
the
T-lang
label
Aug 3, 2018
petrochenkov
reviewed
Aug 3, 2018
| # Summary | ||
| [summary]: #summary | ||
|
|
||
| Unions do not allow fields of types that require drop glue, but they may still |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Aug 3, 2018
Contributor
Nit: "drop glue" is such a rustc-specific jargon, perhaps "trivial destructor drop", or "drop not running any code", or something like this could be better.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
RalfJung
Aug 6, 2018
•
Author
Member
Don't we have a term for "the code that runs when something is dropped"? I feel that is not such a strange rustc-specific concept, all Rust implementations and also e.g. C++ have it.
Personally, I find "noop drop" much less clear than "does not have drop glue".^^
petrochenkov
reviewed
Aug 3, 2018
| (which this RFC adapts from @petrochenkov's proposal) can sometimes be a little | ||
| surprising when looking at individual fields: Whether `u.f2 = ...;` drops | ||
| depends on whether `u.f1` has been previously initialized. @petrochenkov hence | ||
| proposes a lint to warn people that unions with drop-glue fields are not always |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Aug 3, 2018
Contributor
The lint was inherited from the original RFC rather than proposed by me :)
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Aug 3, 2018
| the rules that are currently only applied to unions that `impl Drop`. However, | ||
| that does not actually help with the pitfall described above. The more complex | ||
| rules allow more code that many will reasonably expect to work, and do not seem | ||
| to introduce any additional pitfalls. |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Aug 3, 2018
•
Contributor
Independent nested fields borrowed independently is probably the most natural expectation:
union U { a: (u8, u8), b: u16 }
let x = &u.a.0;
let y = &u.a.1;so it's borrow checker that needs to work in per-field fashion first of all.
Move checker just mirrors what borrow checker does (for consistency and also because they share common infrastructure in the compiler).
This comment has been minimized.
This comment has been minimized.
RalfJung
Aug 6, 2018
Author
Member
Ah, I hadn't thought about borrowck here. So with the rules as stated, once a field is (partially) borrowed, its siblings all become completely blocked from borrowing?
Borrowing is unsafe, so this would not be necessary, but it still seems useful.
This comment has been minimized.
This comment has been minimized.
|
LGTM. This RFC doesn't seem to prevent reintroducing |
This comment has been minimized.
This comment has been minimized.
dlight
commented
Aug 4, 2018
|
The RFC summary should probably link to something that explains what a drop glue is. (The Drop chapter of the Rust book doesn't contain the word "glue") |
RalfJung
added some commits
Aug 6, 2018
This comment has been minimized.
This comment has been minimized.
What would be a good example?
Yes. (I avoided calling them "Drop fields" because a field can need drop glue even if it does not
I added a brief explanation. |
nikomatsakis
assigned
joshtriplett
Aug 9, 2018
This comment has been minimized.
This comment has been minimized.
|
@rfcbot merge Based on discussion in the lang team and with Ralf, I think this is ready to merge. Fields in unions that impl Drop have never been part of stable Rust, so this isn't a breaking change, and this fixes various concerns and painful corner cases that kept coming up. |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Aug 9, 2018
•
|
Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged teams: Concerns:
Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
rfcbot
added
proposed-final-comment-period
disposition-merge
labels
Aug 9, 2018
cramertj
reviewed
Aug 13, 2018
cramertj
reviewed
Aug 13, 2018
| } | ||
| { | ||
| let u = U { f1: ManuallyDrop::new(Vec::new()) }; | ||
| foo(u.f2); |
This comment has been minimized.
This comment has been minimized.
cramertj
Aug 13, 2018
Member
This looks like moving out of a union that implements Drop-- how is this different from the let v = u.f1; case above?
This comment has been minimized.
This comment has been minimized.
RalfJung
Aug 13, 2018
Author
Member
You are right, I will fix the example.
The only way to deinitialize a union with drop is to move away the entire thing, just like with structs.
cramertj
reviewed
Aug 13, 2018
| This requires `unsafe` because it desugars to `ManuallyDrop::deref_mut(&mut u.f).0`, | ||
| and while writing to a union field is safe, taking a reference is not. | ||
|
|
||
| For this reason, `DerefMut` auto-deref is not applied when working on a union or |
This comment has been minimized.
This comment has been minimized.
cramertj
Aug 13, 2018
Member
Why ban just auto-deref rather than banning DerefMut entirely? Users could access the nested types using .as_ptr() or .as_mut_ptr() methods.
This comment has been minimized.
This comment has been minimized.
RalfJung
Aug 13, 2018
Author
Member
I felt that was too drastic, but sure.
Probably calling deref_mut manually is also still okay, just the sugar that lets you use * is not?
I also do not know what in technically feasible in this space.
This comment has been minimized.
This comment has been minimized.
cramertj
Aug 13, 2018
Member
Yeah, I'm not sure what the right balance is. You want to allow the deref when the value has been initialized, but ban it as much as possible in cases where it creates an &mut T to a partially or wholly uninitialized T.
This comment has been minimized.
This comment has been minimized.
RalfJung
Sep 17, 2018
Author
Member
Uh, I think I'd rather avoid such stateful lints. They are not complete enough IMHO (they cannot know which field you are now allowed to create a reference to).
This comment has been minimized.
This comment has been minimized.
|
@rfcbot concern pnkfelix-wants-a-chance-to-read-this-before-he-checks-his-box I've been swamped, but this is an area I sort of care about and I want to read this |
rfcbot
added
proposed-final-comment-period
disposition-merge
labels
Sep 27, 2018
This comment has been minimized.
This comment has been minimized.
|
@rfcbot resolve pnkfelix-wants-a-chance-to-read-this-before-he-checks-his-box read it, looks good. |
rfcbot
added
the
final-comment-period
label
Oct 4, 2018
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Oct 4, 2018
|
|
rfcbot
removed
the
proposed-final-comment-period
label
Oct 4, 2018
daboross
reviewed
Oct 10, 2018
| // Rejected | ||
| union Example4<T> { | ||
| // `T` might have drop glue, and then `Cell<T>` would as well. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
RalfJung
Oct 11, 2018
•
Author
Member
Why that? I didn't intend to list every type with interior mutability.
This comment has been minimized.
This comment has been minimized.
daboross
Oct 11, 2018
Specifically because the line below is f1: RefCell<T>. I assumed this comment was here to explain why, given that line, this struct is rejected.
This comment has been minimized.
This comment has been minimized.
RalfJung
Oct 12, 2018
Author
Member
Oh, I made a typo and didn't use the same type on both sides. Thanks for pointing that out! (GitHub's diff viw for comments embedded in the discussions is so bad...)
This comment has been minimized.
This comment has been minimized.
daboross
Oct 12, 2018
No problem - sorry I didn't point it out better initially! I'm still not entirely used to how github displays these things either.
This comment has been minimized.
This comment has been minimized.
crlf0710
commented
Oct 13, 2018
|
Sorry it's a bit late, but in prior art section: Just want to point out that actually C++ does have destructors for unions. (See https://en.cppreference.com/w/cpp/language/union). Maybe a small edit to the text will be good. |
This comment has been minimized.
This comment has been minimized.
|
@crlf0710 Thanks for pointing that out! I fixed the text. Do you agree it is correct now? |
This comment has been minimized.
This comment has been minimized.
crlf0710
commented
Oct 13, 2018
|
@RalfJung Looks good! |
This comment has been minimized.
This comment has been minimized.
|
Would this auto trait be an accurate expression of the “has no drop glue” concept? pub auto trait NoDropGlue {}
impl<T> !NoDropGlue for T where T: Drop {}If it is and we make it a lang item (in union Foo<T: NoDropGlue> {
f: T,
} |
This comment has been minimized.
This comment has been minimized.
|
I think it would begin to express that concept if it worked, but AFAIK those kind of negative bounds don't work...at least I recall that was the result of someone checking last time. We'd however also need impl<T> NoDropGlue for ManuallyDrop<T>and then what about unions themselves? Do they get auto traits the same way everything else does, or are auto traits just never implemented for them? Either way we'd want |
This comment has been minimized.
This comment has been minimized.
|
Good points. So leaving aside the auto-trait definition and assuming it can be implemented with ~compiler magic~ instead, would it be desirable for this trait to exist to enable such generic unions? |
This comment has been minimized.
This comment has been minimized.
|
I would think so, yes. However, introducing such a trait is firmly out of scope for this PR. |
This comment has been minimized.
This comment has been minimized.
crlf0710
commented
Oct 13, 2018
|
i think the “has no drop glue” approximately corresponds to |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Oct 14, 2018
|
The final comment period, with a disposition to merge, as per the review above, is now complete. |
rfcbot
added
finished-final-comment-period
and removed
final-comment-period
labels
Oct 14, 2018
Centril
referenced this pull request
Oct 17, 2018
Open
Tracking issue for RFC 2514, "Union initialization and Drop" #55149
Centril
merged commit e5276df
into
rust-lang:master
Oct 17, 2018
This comment has been minimized.
This comment has been minimized.
|
Huzzah! This RFC has been merged! Tracking issue: rust-lang/rust#55149 |
RalfJung commentedAug 3, 2018
•
edited by Centril
This RFC realizes the second part of https://internals.rust-lang.org/t/pre-rfc-unions-drop-types-and-manuallydrop/8025: Changing unions to no longer allow fields with drop glue, and otherwise describing what we need to settle before unions can be fully stabilized. Unfortunately this got somewhat more complicated than I thought.
As usual I had trouble separating things between the guide-level and the reference-level explanation; I hope this makes sense!
Rendered
Tracking issue