-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deprecate uninitialized in favor of a new MaybeUninit type #1892
Deprecate uninitialized in favor of a new MaybeUninit type #1892
Conversation
cc @nikomatsakis, @arielb1, @eddyb. I know y'all have some thoughts on what to do with all this. |
Calling |
@ubsan, what about the Without this, any (otherwise correct) code that uses |
The compiler bug happened mostly because previously the |
|
||
This trait is automatically implemented for all inhabited types. | ||
|
||
Change the type of `uninitialized` to: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This conflicts somewhat with Inhabited
being an auto-trait (a-la Sized
), or rather T
is already Inhabited
unless specified otherwise (i.e. T: ?Inhabited
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Does !
impl Sized
? Does that even make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mark-i-m Yes, size_of::<!>() == 0
, More generally though, for any given size every element of !
has that size. Similarly, any two elements of !
have the same size. These are both trivially true since !
has no elements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we are defining size_of::<T>() == n
iff for all values v
of T
that v
takes n
bits to express, and since there are no values of T = !
, this vacuously true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically, yeah. There's two subtly different definitions you could give of Sized
but !
satisfies both of them vacuously.
This could be a rather large breaking change depending on how many people are | ||
currently calling `uninitialized::<T>` with a generic `T`. However all such | ||
code is already somewhat future-incompatible as it will malfunction (or panic) | ||
if used with `!`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If Inhabited
is auto-trait, like Sized
, I do not see how that’s problematic in any way.
The author of the crate may expect this change to be private and its effects | ||
contained to within the crate. But in making this change they've also stopped | ||
exporting the `Inhabited` impl, causing potential breakages for downstream | ||
users. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, that’s pretty much the same story with Sized
. I.e. you cannot change pub struct Sized { a: [u8; 42] }
to pub struct Sized { a: [u8] }
.
We pretty much decided in the design sprint to deprecate |
@arielb1 Ah okay. I figured that might too radical for now so I intended this RFC as a (possibly temporary) middle ground. |
@nagisa Sorry if my wording is confusing. |
That’s not true. fn main() {
let x: *const ! = &0 as *const _ as *const _; // imagine this comes as an generic argument from somewhere
let z: ! = unsafe {
::std::mem::read(x) // boom bam blamma
};
} |
Ah true, I hadn't thought of I still think |
match std::panic::catch_unwind(|| { | ||
let val = f(); | ||
unsafe { | ||
(*foo_ref).value = val; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is broken for types with Drop
impls. It should be ptr::write(&mut (*foo_ref).value, val)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I thought we settled on different rules for overwriting union fields for some reason. Thanks for the catch!
``` | ||
|
||
Yet calling this function does not diverge! It just breaks everything then eats | ||
your laundry instead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somehow, this seems preferable to folding my laundry at the moment...
if used with `!`. | ||
|
||
Another drawback is that the `Inhabited` trait leaks private information about | ||
types. Consider a type with the following definition: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not convinced this is more serious than already-existing leaks... for example, you can already find out the size of a type. Is there any fundamental difference with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, it's the same as Sized
in this regard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, that makes sense 😄
Ideally, Rust's type system should have a way of talking about initializedness | ||
statically. In the past there have been proposals for new pointer types which | ||
could safely handle uninitialized data. We should seriously consider pursuing | ||
one of these proposals. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I totally agree! This would make static mut
s much more powerful while still being safe.
@canndrew could you add the alternative from the design sprint? (add I find it compelling, as it removes some magic from the compiler (no special checks or automatic traits for size/inhabitedness) and lets the type system just do "its thing". |
Sorry, I just realised the option of completely deprecating |
Are there any plans to do the same to |
I agree with what you're saying about I'd be (more) okay with Myself, I think the best route is:
|
Could this issue be solved with |
I think the definition of the size of a type discussed in one of the
earlier comments is nicer in that it gracefully handles !
On Feb 12, 2017 11:02 AM, "djzin" <[email protected]> wrote:
Could this issue be solved with ! having no size? If the size of a type is
defined to be ceil(log(n)) where n is the number of possible
representations, then ! should have undefined size, or, if you like,
"negative infinity" size. The main issue with this is that empty enums are
already defined as being sized... and also "negative infinity" is not
representable in a usize... but just a thought I had ;)
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1892 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AIazwIFjJbuaydpz5kxDgk8hWjqJSt9Xks5rbzsGgaJpZM4L8U6F>
.
|
@ubsan That makes sense. I guess one would need to drop the object instead, the storage after the |
Well, there are other people saying the exact opposite. ;) We have to make a choice either way, but for both cases we have people arguing that this is clearly the more intuitive option.
No, there would be no incompatible change. As @ubsan mentioned, we have two invariants at play here: Which assumptions the compiler can make for its optimizations, and which assumptions safe code can make for values it sees. There is no a priori reason to think these are the same, and in fact I think it is rather impossible to make them the same. I have a blogpost upcoming for this that should hopefully be done no later than Monday... But just one example: A safe higher-order function which takes an argument There are other problems as well. For example, the invariant that may be assumed by safe code is impossible to check for because it is frequently not computable (as in, would require solving the halting problem). I think we should have a definition of UB that can, at least in principle, be checked. |
which has been proposed in rust-lang/rfcs#1892
55: internally use MaybeUninit r=japaric a=japaric which has been proposed in rust-lang/rfcs#1892 Co-authored-by: Jorge Aparicio <[email protected]>
The final comment period, with a disposition to merge, as per the review above, is now complete. |
Huzzah! This RFC has been merged! Tracking issue: rust-lang/rust#53491 |
which has been proposed in rust-lang/rfcs#1892
stabilize core parts of MaybeUninit and deprecate mem::uninitialized in the future (1.40.0). This is part of implementing rust-lang/rfcs#1892. Also expand the documentation a bit. This type is currently primarily useful when dealing with partially initialized arrays. In libstd, it is used e.g. in `BTreeMap` (with some unstable APIs that however can all be replaced, less ergonomically, by stable ones). What we stabilize should also be enough for `SmallVec` (Cc @bluss). Making this useful for structs requires rust-lang/rfcs#2582 or a commitment that references to uninitialized data are not insta-UB.
My thoughts on what to do with
uninitialized
and!
.Rendered
Tracking issue
Edit: This has been updated to instead recommend deprecating
uninitialized
entirely. The oldInhabited
trait proposal is now listed as an alternative.Edit: FCP Proposal is #1892 (comment) (in the collapsed-by-default part).