From ec78a805ff835db054b85287f7846c7d68546f0d Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 9 Feb 2017 23:17:51 +0800 Subject: [PATCH 01/19] Add uninitialized/uninhabited RFC --- text/0000-uninitialized-uninhabited.md | 224 +++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 text/0000-uninitialized-uninhabited.md diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md new file mode 100644 index 00000000000..97151adc4c1 --- /dev/null +++ b/text/0000-uninitialized-uninhabited.md @@ -0,0 +1,224 @@ +- Feature Name: uninitialized-uninhabited +- Start Date: 2017-02-09 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Deprecate the usage of `mem::uninitialized` for possibly-uninhabited types. +Specifically: + * Add a built-in `Inhabited` trait which is automatically implemented for + types known to have at least 1 possible value. + * Require an `Inhabited` bound on `T` for `uninitialized::`. + * Add a `MaybeUninit` union to the standard library for representing + possibly-initialized data in a more type-system-friendly way. + +# Motivation +[motivation]: #motivation + +The concept of "uninitialized data" is extremely problematic when it comes into +contact with uninhabited types. + +For any given type there may be valid and invalid bit-representations. For +example, the type `u8` consists of a single byte, and all possible bytes can be +sensibly interpreted as a value of type `u8`. By contrast, a `bool` also +consists of a single byte, but not all bytes represent a `bool`: the +bit-patterns `[00000000]` (false) and `[00000001]` (true) are valid `bool`s +whereas `[00101010]` is not. By further contrast, the type `!` has no valid +bit-representations at all. Even though it's treated as a zero-sized type, the +empty bit-buffer `[]` is not a valid representation and has no interpretation +as a `!`. + +As `bool` has both valid and invalid bit-representations, an uninitialized +`bool` cannot be known to be invalid until it is inspected. At this point - if +it is invalid - the compiler is free to invoke undefined behaviour. By +contrast, an uninitialized `!` can only possibly be invalid. Without even +inspecting such a value the compiler can assume that it's working in an +impossible state-of-affairs just by having such a value in scope. This is the +logical basis for using a return type of `!` to represent diverging functions. +If we call a function which returns `bool` we can't assume that the returned +value is invalid. However, if a function call returns `!` we know that the +function cannot sensibly return. Therefore we can treat everything after the +call as dead code and if the function **does** return we can write off that +scenario as undefined behaviour. + +The issue then is what to do about `uninitialized::()`? +`uninitialized::` is meaningless for uninhabited `T` and is currently +automatic undefined behaviour when `T = !`, even if the "value of type !` is +never read. The type signature of `uninitialized::` is, after all, that of a +diverging function: + +```rust +mem::uninitialized::() -> ! +``` + +Yet the function call does not diverge! It just breaks everything instead. + +In this RFC, I propose restricting `uninitialized` to use with types that are +known to be inhabited. I also propose an addition to the standard library of a +`MaybeUninit` type which offers a much more principled way of handling +uninitialized data and can be used sensibly with uninhabited types. + +# Detailed design +[design]: #detailed-design + +Add the following trait as a lang item: + +```rust +#[lang="inhabited"] +trait Inhabited {} +``` + +This trait is automatically implemented for inhabited types. + +Change the type of `uninitialized` to: + +```rust +pub unsafe fn uninitialized() -> T +``` + +Before enforcing this change we should have a future-compatibility warning +cycle and urge people to switch to `MaybeUninit` where possible. + +Add a new type to the standard library: + +```rust +union MaybeUninit { + uninit: (), + value: T, +} +``` + +For an example of how this type can replace `uninitialized` consider the +following code: + +```rust +fn catch_an_unwind T>(f: F) -> Option { + let mut foo = unsafe { + mem::uninitialized::() + }; + let mut foo_ref = &mut foo as *mut T; + + match std::panic::catch_unwind(|| { + let val = f(); + unsafe { + ptr::write(foo_ref, val); + } + }) { + Ok(()) => Some(foo); + Err(_) => None + } +} +``` + +The problem here is, by the time we get to the second line we're already saying +we have a value of type `T`, which we don't, and which for `T = !` is +impossible. We can use `MaybeUninit` instead like this: + +```rust +fn catch_an_unwind T>(f: F) -> Option { + let mut foo: MaybeUninit = MaybeUninit { + uninit: (), + }; + let mut foo_ref = &mut foo as *mut MaybeUninit; + + match std::panic::catch_unwind(|| { + let val = f(); + unsafe { + (*foo_ref).value = val; + } + }) { + Ok(()) => { + unsafe { + Some(foo.value) + } + }, + Err(_) => None + } +} +``` + +Here, we've moved the `unsafe` block to where we actually know we have a `T`. +This is fine to use with `!` because we can never reach this line (we will +always take the `Err` branch). + +# How We Teach This +[how-we-teach-this]: #how-we-teach-this + +Correct handling of uninitialized data is an advanced topic and should maybe be +left to The Rustinomicon. + +The documentation for `uninitialized` however should explain the motivation for +these changes and direct people to the `MaybeUninit` type. + +# Drawbacks +[drawbacks]: #drawbacks + +This could be a rather large breaking change depending on how many people are +currently calling `uninitialized::` with a generic `T`. However all such +code is already somewhat future-incompatible as it will malfunction (or panic) +if used with `!`. + +Another drawback is that the `Inhabited` trait leaks private information about +types. Consider a type with the following definition: + +```rust +pub struct AmIInhabited { + _priv: (), +} +``` + +If this type is exported from its crate or module, it also exports an impl for +`Inhabited` with it. Now suppose the definition is changed to: + +```rust +pub struct AmIInhabited { + _priv: !, +} +``` + +The author of the crate may expect this change to be private and its effects +contained to the crate. But in making the change they've also stopped exporting +the `Inhabited` impl. This could (potentially) break downstream crates. + +Although this is a problem in principal it's unlikely to come up much in +practice. It would be strange for someone to change an inhabited exported type +to being uninhabited. And any library consumers would already be unable to use +`uninitialized` with a generic `T`, they'd have to be using it with the +exported type specifically to hit the regression. + +# Alternatives +[alternatives]: #alternatives + +* Not do this. +* Just make `uninitialized::` panic instead (making `!`'s behaviour + suprisingly inconsitent with all the other types). +* Adopt these rules but not the `Inhabited` trait. Instead make `uninitialized` + behave like `transmute` does today by having restrictions on its type + arguments that are enforced outside the trait system. +* Not add the `Inhabited` trait. Instead add `MaybeUninit` as a lang item, + adopt the `Transmute` trait RFC, and replace the `Inhabited` bound on + `uninitialized::` with `MaybeUninit: Transmute` to get the same + effect. +* Rename `Inhabited` to `Uninitialized` and add the `uninitialized` function as + an unsafe method to the trait. + +# Unresolved questions +[unresolved]: #unresolved-questions + +None known. + +# Future directions + +Ideally, Rust's type system should have a way of talking about initializedness +statically. In the past there have been propsals for new pointer types which +could safely handle uninitialized data. We should seriously consider pursuing +one of these proposals. + +This RFC could be a possible stepping-stone to completely deprecating +`uninitialized`. We would need to see how `MaybeUninit` is used in practice +beforehand, and it would be nice (though not strictly necessary) to implement +the additional pointer types beforehand aswell so that users of `uninitialized` +have the best options to migrate to. + From c5c75b6ab509d802e754c960585ba267cd7bd325 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 9 Feb 2017 23:49:07 +0800 Subject: [PATCH 02/19] Grammar fixes etc. --- text/0000-uninitialized-uninhabited.md | 114 +++++++++++++------------ 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 97151adc4c1..1c41f1b2fd8 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -20,45 +20,47 @@ Specifically: The concept of "uninitialized data" is extremely problematic when it comes into contact with uninhabited types. -For any given type there may be valid and invalid bit-representations. For -example, the type `u8` consists of a single byte, and all possible bytes can be +For any given type, there may be valid and invalid bit-representations. For +example, the type `u8` consists of a single byte and all possible bytes can be sensibly interpreted as a value of type `u8`. By contrast, a `bool` also -consists of a single byte, but not all bytes represent a `bool`: the -bit-patterns `[00000000]` (false) and `[00000001]` (true) are valid `bool`s +consists of a single byte but not all bytes represent a `bool`: the +bit vectors `[00000000]` (`false`) and `[00000001]` (`true`) are valid `bool`s whereas `[00101010]` is not. By further contrast, the type `!` has no valid bit-representations at all. Even though it's treated as a zero-sized type, the -empty bit-buffer `[]` is not a valid representation and has no interpretation +empty bit vector `[]` is not a valid representation and has no interpretation as a `!`. As `bool` has both valid and invalid bit-representations, an uninitialized -`bool` cannot be known to be invalid until it is inspected. At this point - if -it is invalid - the compiler is free to invoke undefined behaviour. By -contrast, an uninitialized `!` can only possibly be invalid. Without even -inspecting such a value the compiler can assume that it's working in an -impossible state-of-affairs just by having such a value in scope. This is the -logical basis for using a return type of `!` to represent diverging functions. -If we call a function which returns `bool` we can't assume that the returned -value is invalid. However, if a function call returns `!` we know that the -function cannot sensibly return. Therefore we can treat everything after the -call as dead code and if the function **does** return we can write off that -scenario as undefined behaviour. - -The issue then is what to do about `uninitialized::()`? +`bool` cannot be known to be invalid until it is inspected. At this point, if +it is invalid, the compiler is free to invoke undefined behaviour. By contrast, +an uninitialized `!` can only possibly be invalid. Without even inspecting such +a value the compiler can assume that it's working in an impossible +state-of-affairs whenever such a value is in scope. This is the logical basis +for using a return type of `!` to represent diverging functions. If we call a +function which returns `bool` we can't assume that the returned value is +invalid and we have to handle the possibility that the function returns. +However if a function call returns `!`, we know that the function cannot +sensibly return. Therefore we can treat everything after the call as dead code +and we can write-off the scenario where the function *does* return as being +undefined behaviour. + +The issue then is what to do about `uninitialized::()` where `T = !`? `uninitialized::` is meaningless for uninhabited `T` and is currently -automatic undefined behaviour when `T = !`, even if the "value of type !` is +automatic undefined behaviour when `T = !` - even if the "value of type `!`" is never read. The type signature of `uninitialized::` is, after all, that of a diverging function: ```rust -mem::uninitialized::() -> ! +fn mem::uninitialized::() -> ! ``` -Yet the function call does not diverge! It just breaks everything instead. +Yet calling this function does not diverge! It just breaks everything instead. -In this RFC, I propose restricting `uninitialized` to use with types that are +This RFC proposes restricting the use of `uninitialized` to types that are known to be inhabited. I also propose an addition to the standard library of a `MaybeUninit` type which offers a much more principled way of handling -uninitialized data and can be used sensibly with uninhabited types. +uninitialized data and can be used with uninhabited types without lying to the +type system. # Detailed design [design]: #detailed-design @@ -70,7 +72,7 @@ Add the following trait as a lang item: trait Inhabited {} ``` -This trait is automatically implemented for inhabited types. +This trait is automatically implemented for all inhabited types. Change the type of `uninitialized` to: @@ -90,7 +92,7 @@ union MaybeUninit { } ``` -For an example of how this type can replace `uninitialized` consider the +For an example of how this type can replace `uninitialized`, consider the following code: ```rust @@ -112,8 +114,8 @@ fn catch_an_unwind T>(f: F) -> Option { } ``` -The problem here is, by the time we get to the second line we're already saying -we have a value of type `T`, which we don't, and which for `T = !` is +The problem here is, by the time we get to the third line we're already saying +we have a value of type `T`; which we don't, and which for `T = !` is impossible. We can use `MaybeUninit` instead like this: ```rust @@ -139,18 +141,19 @@ fn catch_an_unwind T>(f: F) -> Option { } ``` -Here, we've moved the `unsafe` block to where we actually know we have a `T`. -This is fine to use with `!` because we can never reach this line (we will -always take the `Err` branch). +Here, we've moved the `unsafe` block to the return position where we actually +know we have a `T`. This is fine for use with `!` because we can only reach +the line that extracts `value` from `foo` if `f` returned normally (which it +couldn't have). # How We Teach This [how-we-teach-this]: #how-we-teach-this -Correct handling of uninitialized data is an advanced topic and should maybe be -left to The Rustinomicon. +Correct handling of uninitialized data is an advanced topic and should probably be +left to The Rustonomicon. -The documentation for `uninitialized` however should explain the motivation for -these changes and direct people to the `MaybeUninit` type. +The documentation for `uninitialized` should explain the motivation for these +changes and direct people to the `MaybeUninit` type. # Drawbacks [drawbacks]: #drawbacks @@ -169,8 +172,8 @@ pub struct AmIInhabited { } ``` -If this type is exported from its crate or module, it also exports an impl for -`Inhabited` with it. Now suppose the definition is changed to: +If this type is exported from its crate or module it will also export an impl +for `Inhabited`. Now suppose the definition is changed to: ```rust pub struct AmIInhabited { @@ -179,30 +182,35 @@ pub struct AmIInhabited { ``` The author of the crate may expect this change to be private and its effects -contained to the crate. But in making the change they've also stopped exporting -the `Inhabited` impl. This could (potentially) break downstream crates. +contained to within the crate. But in making this change they've also stopped +exporting the `Inhabited` impl, causing potential breakages for downstream +users. Although this is a problem in principal it's unlikely to come up much in -practice. It would be strange for someone to change an inhabited exported type -to being uninhabited. And any library consumers would already be unable to use +practice. It can't be common for someone to change an inhabited exported type +to being uninhabited; and any library consumers would already be unable to use `uninitialized` with a generic `T`, they'd have to be using it with the -exported type specifically to hit the regression. +exported type specifically to hit a regression. # Alternatives [alternatives]: #alternatives * Not do this. * Just make `uninitialized::` panic instead (making `!`'s behaviour - suprisingly inconsitent with all the other types). + surprisingly inconsistent with all the other types). * Adopt these rules but not the `Inhabited` trait. Instead make `uninitialized` - behave like `transmute` does today by having restrictions on its type - arguments that are enforced outside the trait system. + behave like `transmute` does today by having the restrictions on its type + arguments enforced outside the trait system. * Not add the `Inhabited` trait. Instead add `MaybeUninit` as a lang item, - adopt the `Transmute` trait RFC, and replace the `Inhabited` bound on - `uninitialized::` with `MaybeUninit: Transmute` to get the same - effect. -* Rename `Inhabited` to `Uninitialized` and add the `uninitialized` function as - an unsafe method to the trait. + adopt the `Transmute` trait RFC, and change the signature of `uninitialized` to + + ```rust + pub unsafe fn uninitialized() -> T + where MaybeUninit: Transmute + ``` + to get the same effect. +* Rename `Inhabited` to `Uninitialized` and move the `uninitialized` function + to being an unsafe method on the trait. # Unresolved questions [unresolved]: #unresolved-questions @@ -212,13 +220,13 @@ None known. # Future directions Ideally, Rust's type system should have a way of talking about initializedness -statically. In the past there have been propsals for new pointer types which +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. This RFC could be a possible stepping-stone to completely deprecating `uninitialized`. We would need to see how `MaybeUninit` is used in practice -beforehand, and it would be nice (though not strictly necessary) to implement -the additional pointer types beforehand aswell so that users of `uninitialized` -have the best options to migrate to. +beforehand, and it would be nice (though not strictly necessary) to also +implement the additional pointer types beforehand so that users of +`uninitialized` have the best possible available options to migrate to. From d449f8508120486cde6121184960969494fd337a Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Fri, 10 Feb 2017 00:00:55 +0800 Subject: [PATCH 03/19] More minor fixes --- text/0000-uninitialized-uninhabited.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 1c41f1b2fd8..1a0fbd25e8a 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -6,13 +6,13 @@ # Summary [summary]: #summary -Deprecate the usage of `mem::uninitialized` for possibly-uninhabited types. +Deprecate the usage of `mem::uninitialized` with possibly-uninhabited types. Specifically: * Add a built-in `Inhabited` trait which is automatically implemented for types known to have at least 1 possible value. * Require an `Inhabited` bound on `T` for `uninitialized::`. * Add a `MaybeUninit` union to the standard library for representing - possibly-initialized data in a more type-system-friendly way. + possibly-initialized data in a more type-system-aware way. # Motivation [motivation]: #motivation @@ -37,7 +37,7 @@ an uninitialized `!` can only possibly be invalid. Without even inspecting such a value the compiler can assume that it's working in an impossible state-of-affairs whenever such a value is in scope. This is the logical basis for using a return type of `!` to represent diverging functions. If we call a -function which returns `bool` we can't assume that the returned value is +function which returns `bool`, we can't assume that the returned value is invalid and we have to handle the possibility that the function returns. However if a function call returns `!`, we know that the function cannot sensibly return. Therefore we can treat everything after the call as dead code @@ -46,7 +46,7 @@ undefined behaviour. The issue then is what to do about `uninitialized::()` where `T = !`? `uninitialized::` is meaningless for uninhabited `T` and is currently -automatic undefined behaviour when `T = !` - even if the "value of type `!`" is +instant undefined behaviour when `T = !` - even if the "value of type `!`" is never read. The type signature of `uninitialized::` is, after all, that of a diverging function: @@ -54,13 +54,14 @@ diverging function: fn mem::uninitialized::() -> ! ``` -Yet calling this function does not diverge! It just breaks everything instead. +Yet calling this function does not diverge! It just breaks everything then eats +your laundry instead. This RFC proposes restricting the use of `uninitialized` to types that are known to be inhabited. I also propose an addition to the standard library of a `MaybeUninit` type which offers a much more principled way of handling -uninitialized data and can be used with uninhabited types without lying to the -type system. +uninitialized data and can be used to hold uninitialized uninhabited types +without lying to the type system. # Detailed design [design]: #detailed-design @@ -187,7 +188,7 @@ exporting the `Inhabited` impl, causing potential breakages for downstream users. Although this is a problem in principal it's unlikely to come up much in -practice. It can't be common for someone to change an inhabited exported type +practice. I doubt it's common for someone to change an inhabited exported type to being uninhabited; and any library consumers would already be unable to use `uninitialized` with a generic `T`, they'd have to be using it with the exported type specifically to hit a regression. @@ -228,5 +229,5 @@ This RFC could be a possible stepping-stone to completely deprecating `uninitialized`. We would need to see how `MaybeUninit` is used in practice beforehand, and it would be nice (though not strictly necessary) to also implement the additional pointer types beforehand so that users of -`uninitialized` have the best possible available options to migrate to. +`uninitialized` have the best possible options available to migrate to. From aae2944f6e8bc71496812ce53315779e7b531405 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Fri, 10 Feb 2017 00:12:12 +0800 Subject: [PATCH 04/19] Nah, that still doesn't read right --- text/0000-uninitialized-uninhabited.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 1a0fbd25e8a..3aa5458e702 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -6,7 +6,7 @@ # Summary [summary]: #summary -Deprecate the usage of `mem::uninitialized` with possibly-uninhabited types. +Deprecate the usage of `mem::uninitialized::` for possibly-uninhabited `T`. Specifically: * Add a built-in `Inhabited` trait which is automatically implemented for types known to have at least 1 possible value. From 874167935999f8bab50906ab060c85879ca580ee Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Fri, 10 Feb 2017 12:00:55 +0800 Subject: [PATCH 05/19] Fix MaybeUninit example --- text/0000-uninitialized-uninhabited.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 3aa5458e702..17cc0411dc4 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -129,7 +129,7 @@ fn catch_an_unwind T>(f: F) -> Option { match std::panic::catch_unwind(|| { let val = f(); unsafe { - (*foo_ref).value = val; + ptr::write(&mut (*foo_ref).value, val); } }) { Ok(()) => { From 075b62c2e7de36e0df5e7f4d1d35aaed6d01da94 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Fri, 10 Feb 2017 16:37:56 +0800 Subject: [PATCH 06/19] Mention deprecating uninitialized as an alternative --- text/0000-uninitialized-uninhabited.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 17cc0411dc4..f265d2c6bd3 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -197,6 +197,7 @@ exported type specifically to hit a regression. [alternatives]: #alternatives * Not do this. +* Deprecate `uninitialized` entirely and force people on to `MaybeUninit`. * Just make `uninitialized::` panic instead (making `!`'s behaviour surprisingly inconsistent with all the other types). * Adopt these rules but not the `Inhabited` trait. Instead make `uninitialized` From f1e7e3dba8a2004120264004bc2fb8d3cdfca7fa Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 16 Feb 2017 12:24:08 +0800 Subject: [PATCH 07/19] Small additions based on feedback --- text/0000-uninitialized-uninhabited.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index f265d2c6bd3..a03969f8467 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -150,8 +150,12 @@ couldn't have). # How We Teach This [how-we-teach-this]: #how-we-teach-this -Correct handling of uninitialized data is an advanced topic and should probably be -left to The Rustonomicon. +Correct handling of uninitialized data is an advanced topic and should probably +be left to The Rustonomicon. + +The Rust Book should probably have a section on uninhabited types, explaining +what they are and some of their properties. This should section should make +mention of the `Inhabited` trait. The documentation for `uninitialized` should explain the motivation for these changes and direct people to the `MaybeUninit` type. @@ -191,7 +195,8 @@ Although this is a problem in principal it's unlikely to come up much in practice. I doubt it's common for someone to change an inhabited exported type to being uninhabited; and any library consumers would already be unable to use `uninitialized` with a generic `T`, they'd have to be using it with the -exported type specifically to hit a regression. +exported type specifically to hit a regression. Also, the same problem affects +the `Sized` trait but so far hasn't cause any major issues. # Alternatives [alternatives]: #alternatives From 12a009369abdaff77a713220044aa2c90834218d Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 10 Aug 2017 01:54:06 +0800 Subject: [PATCH 08/19] Fix typos --- text/0000-uninitialized-uninhabited.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index a03969f8467..41ab881d2cb 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -154,7 +154,7 @@ Correct handling of uninitialized data is an advanced topic and should probably be left to The Rustonomicon. The Rust Book should probably have a section on uninhabited types, explaining -what they are and some of their properties. This should section should make +what they are and some of their properties. This short section should make mention of the `Inhabited` trait. The documentation for `uninitialized` should explain the motivation for these @@ -191,7 +191,7 @@ contained to within the crate. But in making this change they've also stopped exporting the `Inhabited` impl, causing potential breakages for downstream users. -Although this is a problem in principal it's unlikely to come up much in +Although this is a problem in principle it's unlikely to come up much in practice. I doubt it's common for someone to change an inhabited exported type to being uninhabited; and any library consumers would already be unable to use `uninitialized` with a generic `T`, they'd have to be using it with the From c753c7326bad33379cf1bd3c109e46b057feb330 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Wed, 6 Sep 2017 14:03:02 +0800 Subject: [PATCH 09/19] Remove Inhabited trait, just deprecate uninitialzed --- text/0000-uninitialized-uninhabited.md | 167 +++++++++---------------- 1 file changed, 62 insertions(+), 105 deletions(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 41ab881d2cb..c9ff8f44765 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -6,19 +6,15 @@ # Summary [summary]: #summary -Deprecate the usage of `mem::uninitialized::` for possibly-uninhabited `T`. -Specifically: - * Add a built-in `Inhabited` trait which is automatically implemented for - types known to have at least 1 possible value. - * Require an `Inhabited` bound on `T` for `uninitialized::`. - * Add a `MaybeUninit` union to the standard library for representing - possibly-initialized data in a more type-system-aware way. +Deprecate `mem::uninitialized::` and replace it with a `MaybeUninit` type +for safer and more principled handling of uninitialized data. # Motivation [motivation]: #motivation -The concept of "uninitialized data" is extremely problematic when it comes into -contact with uninhabited types. +The problems with `uninitialized` centre around its usage with uninhabited +types. The concept of "uninitialized data" is extremely problematic when it +comes into contact with types like `!` or `Void`. For any given type, there may be valid and invalid bit-representations. For example, the type `u8` consists of a single byte and all possible bytes can be @@ -57,34 +53,7 @@ fn mem::uninitialized::() -> ! Yet calling this function does not diverge! It just breaks everything then eats your laundry instead. -This RFC proposes restricting the use of `uninitialized` to types that are -known to be inhabited. I also propose an addition to the standard library of a -`MaybeUninit` type which offers a much more principled way of handling -uninitialized data and can be used to hold uninitialized uninhabited types -without lying to the type system. - -# Detailed design -[design]: #detailed-design - -Add the following trait as a lang item: - -```rust -#[lang="inhabited"] -trait Inhabited {} -``` - -This trait is automatically implemented for all inhabited types. - -Change the type of `uninitialized` to: - -```rust -pub unsafe fn uninitialized() -> T -``` - -Before enforcing this change we should have a future-compatibility warning -cycle and urge people to switch to `MaybeUninit` where possible. - -Add a new type to the standard library: +An alternative way of representing uninitialized data is through a union type: ```rust union MaybeUninit { @@ -93,8 +62,18 @@ union MaybeUninit { } ``` -For an example of how this type can replace `uninitialized`, consider the -following code: +Instead of creating an "uninitialized value", we can create a `MaybeUninit` +initialized with `uninit = ()`. Then, once we know that the value in the union +is valid, we can extract it with `my_uninit.value`. This is a better way of +handling uninitialized data because it doesn't involve lying to the type system +and pretending that we have a value when we don't. It also better represents +what's actually going on: we never *really* have a value of type `T` when we're +using `uninitialized::`, what we have is some memory that contains either a +value (`value: T`) or nothing (`uninit: ()`), with it being the programmer's +responsibility to keep track of which state we're in. + +To see how this can replace `uninitialized` and fix bugs in the process, +consider the following code: ```rust fn catch_an_unwind T>(f: F) -> Option { @@ -115,9 +94,13 @@ fn catch_an_unwind T>(f: F) -> Option { } ``` -The problem here is, by the time we get to the third line we're already saying -we have a value of type `T`; which we don't, and which for `T = !` is -impossible. We can use `MaybeUninit` instead like this: +Naively, this code might look safe. The problem though is that by the time we +get to `let mut foo_ref` we're already saying we have a value of type `T`. But +we don't, and for `T = !` this is impossible. If this function is called with a +diverging callback then it will invoke undefined behaviour before it even gets +to `catch_unwind`. + +We can fix this by using `MaybeUninit` instead: ```rust fn catch_an_unwind T>(f: F) -> Option { @@ -142,20 +125,40 @@ fn catch_an_unwind T>(f: F) -> Option { } ``` -Here, we've moved the `unsafe` block to the return position where we actually -know we have a `T`. This is fine for use with `!` because we can only reach -the line that extracts `value` from `foo` if `f` returned normally (which it -couldn't have). +Note the difference: here we're using unsafe in the part of the code which is +actually unsafe - where we have to assert to the compiler that we have a valid +value. And we only ever tell the compiler we have a value of type `T` where we +know we actually do have a value of type `T`. As such, this is fine to use with +any `T`, including `!`. If the callback diverges then it's not possible to get +to the `unsafe` block and try to read the non-existant value. + +Given that it's so easy for code using `uninitialzed` to hide bugs like this, +and given that there's a better alternative, this RFC proposes deprecating +`uninitialized` and introducing the `MaybeUninit` type into the standard +library. + +# Detailed design +[design]: #detailed-design + +Add the aforementioned `MaybeUninit` type to the standard library: + +```rust +union MaybeUninit { + uninit: (), + value: T, +} +``` + +Deprecate `uninitialized` with a deprecation messages that points people to the +`MaybeUninit` type. Make calling `uninitialized` on an empty type trigger a +runtime panic. # How We Teach This [how-we-teach-this]: #how-we-teach-this Correct handling of uninitialized data is an advanced topic and should probably -be left to The Rustonomicon. - -The Rust Book should probably have a section on uninhabited types, explaining -what they are and some of their properties. This short section should make -mention of the `Inhabited` trait. +be left to The Rustonomicon. There should be a paragraph somewhere therein +introducing the `MaybeUninit` type. The documentation for `uninitialized` should explain the motivation for these changes and direct people to the `MaybeUninit` type. @@ -163,61 +166,21 @@ changes and direct people to the `MaybeUninit` type. # Drawbacks [drawbacks]: #drawbacks -This could be a rather large breaking change depending on how many people are -currently calling `uninitialized::` with a generic `T`. However all such -code is already somewhat future-incompatible as it will malfunction (or panic) -if used with `!`. - -Another drawback is that the `Inhabited` trait leaks private information about -types. Consider a type with the following definition: - -```rust -pub struct AmIInhabited { - _priv: (), -} -``` - -If this type is exported from its crate or module it will also export an impl -for `Inhabited`. Now suppose the definition is changed to: - -```rust -pub struct AmIInhabited { - _priv: !, -} -``` - -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. - -Although this is a problem in principle it's unlikely to come up much in -practice. I doubt it's common for someone to change an inhabited exported type -to being uninhabited; and any library consumers would already be unable to use -`uninitialized` with a generic `T`, they'd have to be using it with the -exported type specifically to hit a regression. Also, the same problem affects -the `Sized` trait but so far hasn't cause any major issues. +This will be a rather large breaking change as a lot of people are using +`uninitialized`. However, much of this code already likely contains subtle +bugs. # Alternatives [alternatives]: #alternatives * Not do this. -* Deprecate `uninitialized` entirely and force people on to `MaybeUninit`. * Just make `uninitialized::` panic instead (making `!`'s behaviour surprisingly inconsistent with all the other types). -* Adopt these rules but not the `Inhabited` trait. Instead make `uninitialized` - behave like `transmute` does today by having the restrictions on its type - arguments enforced outside the trait system. -* Not add the `Inhabited` trait. Instead add `MaybeUninit` as a lang item, - adopt the `Transmute` trait RFC, and change the signature of `uninitialized` to - - ```rust - pub unsafe fn uninitialized() -> T - where MaybeUninit: Transmute - ``` - to get the same effect. -* Rename `Inhabited` to `Uninitialized` and move the `uninitialized` function - to being an unsafe method on the trait. +* Introduce an `Inhabited` auto-trait for inhabited types and add it as a bound + to the type argument of `uninitialized`. +* Disallow using uninhabited types with `uninitialized` by making it behave + like `transmute` does today - by having restrictions on its type arguments + which are enforced outside the trait system. # Unresolved questions [unresolved]: #unresolved-questions @@ -231,9 +194,3 @@ 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. -This RFC could be a possible stepping-stone to completely deprecating -`uninitialized`. We would need to see how `MaybeUninit` is used in practice -beforehand, and it would be nice (though not strictly necessary) to also -implement the additional pointer types beforehand so that users of -`uninitialized` have the best possible options available to migrate to. - From 5f6c6cde85c9a56d4342b5f0b28a1f6793221929 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Wed, 6 Sep 2017 14:12:52 +0800 Subject: [PATCH 10/19] tweak some wording --- text/0000-uninitialized-uninhabited.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index c9ff8f44765..776efd40278 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -96,9 +96,9 @@ fn catch_an_unwind T>(f: F) -> Option { Naively, this code might look safe. The problem though is that by the time we get to `let mut foo_ref` we're already saying we have a value of type `T`. But -we don't, and for `T = !` this is impossible. If this function is called with a -diverging callback then it will invoke undefined behaviour before it even gets -to `catch_unwind`. +we don't, and for `T = !` this is impossible. And so if this function is called +with a diverging callback it will invoke undefined behaviour before it even +gets to `catch_unwind`. We can fix this by using `MaybeUninit` instead: @@ -125,7 +125,7 @@ fn catch_an_unwind T>(f: F) -> Option { } ``` -Note the difference: here we're using unsafe in the part of the code which is +Note the difference: we've moved the unsafe block to the part of the code which is actually unsafe - where we have to assert to the compiler that we have a valid value. And we only ever tell the compiler we have a value of type `T` where we know we actually do have a value of type `T`. As such, this is fine to use with @@ -135,7 +135,7 @@ to the `unsafe` block and try to read the non-existant value. Given that it's so easy for code using `uninitialzed` to hide bugs like this, and given that there's a better alternative, this RFC proposes deprecating `uninitialized` and introducing the `MaybeUninit` type into the standard -library. +library as a replacement. # Detailed design [design]: #detailed-design @@ -151,7 +151,7 @@ union MaybeUninit { Deprecate `uninitialized` with a deprecation messages that points people to the `MaybeUninit` type. Make calling `uninitialized` on an empty type trigger a -runtime panic. +runtime panic which also prints the deprecation message. # How We Teach This [how-we-teach-this]: #how-we-teach-this From b4a4734b6961adaab13e4ee4cdf3f515fc54d3e2 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Fri, 16 Mar 2018 13:20:51 +0800 Subject: [PATCH 11/19] Add details about MaybeUninit API --- text/0000-uninitialized-uninhabited.md | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 776efd40278..fc8efade245 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -143,12 +143,76 @@ library as a replacement. Add the aforementioned `MaybeUninit` type to the standard library: ```rust +#[repr(transparent)] union MaybeUninit { uninit: (), value: T, } ``` +The type should have at least the following interface + +```rust +impl MaybeUninit { + /// Create a new `MaybeUninit` in an uninitialized state. + pub fn uninitialized() -> MaybeUninit { + MaybeUninit { + uninit: (), + } + } + + /// Set the value of the `MaybeUninit`. The overwrites any previous value without dropping it. + pub fn set(&mut self, val: T) -> &mut T { + unsafe { + self.value = val; + &mut self.value + } + } + + /// Take the value of the `MaybeUninit`, putting it into an uninitialized state. + /// + /// # Unsafety + /// + /// It is up to the caller to guarantee that the the `MaybeUninit` really is in an initialized + /// state, otherwise undefined behaviour will result. + pub unsafe fn get(&self) -> T { + std::ptr::read(&self.value) + } + + /// Get a reference to the contained value. + /// + /// # Unsafety + /// + /// It is up to the caller to guarantee that the the `MaybeUninit` really is in an initialized + /// state, otherwise undefined behaviour will result. + pub unsafe fn get_ref(&self) -> &T { + &self.value + } + + /// Get a mutable reference to the contained value. + /// + /// # Unsafety + /// + /// It is up to the caller to guarantee that the the `MaybeUninit` really is in an initialized + /// state, otherwise undefined behaviour will result. + pub unsafe fn get_mut(&mut self) -> &mut T { + &mut self.value + } + + /// Get a pointer to the contained value. This pointer will only be valid if the `MaybeUninit` + /// is in an initialized state. + pub fn as_ptr(&self) -> *const T { + unsafe { &self.value as *const T } + } + + /// Get a mutable pointer to the contained value. This pointer will only be valid if the + /// `MaybeUninit` is in an initialized state. + pub fn as_mut_ptr(&mut self) -> *mut T { + unsafe { &mut self.value as *mut T } + } +} +``` + Deprecate `uninitialized` with a deprecation messages that points people to the `MaybeUninit` type. Make calling `uninitialized` on an empty type trigger a runtime panic which also prints the deprecation message. From 835f860ab35501d678d9944ee36e87dc2b2b7f31 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Fri, 16 Mar 2018 16:09:37 +0800 Subject: [PATCH 12/19] Fix as_{,mut}_ptr methods --- text/0000-uninitialized-uninhabited.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index fc8efade245..49fb792325d 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -202,13 +202,13 @@ impl MaybeUninit { /// Get a pointer to the contained value. This pointer will only be valid if the `MaybeUninit` /// is in an initialized state. pub fn as_ptr(&self) -> *const T { - unsafe { &self.value as *const T } + self as *const MaybeUninit as *const T } /// Get a mutable pointer to the contained value. This pointer will only be valid if the /// `MaybeUninit` is in an initialized state. pub fn as_mut_ptr(&mut self) -> *mut T { - unsafe { &mut self.value as *mut T } + self as *mut MaybeUninit as *mut T } } ``` From f6649f178300ed0dbeb298ca1941ba73bab527e7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 Aug 2018 15:31:28 +0200 Subject: [PATCH 13/19] discuss that ! is not the only problem; add MaybeUninit::zeroed --- text/0000-uninitialized-uninhabited.md | 36 +++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 49fb792325d..4bba022b3ff 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -6,15 +6,17 @@ # Summary [summary]: #summary -Deprecate `mem::uninitialized::` and replace it with a `MaybeUninit` type -for safer and more principled handling of uninitialized data. +Deprecate `mem::uninitialized::` and `mem::zeroed::` and replace them with +a `MaybeUninit` type for safer and more principled handling of uninitialized +data. # Motivation [motivation]: #motivation The problems with `uninitialized` centre around its usage with uninhabited -types. The concept of "uninitialized data" is extremely problematic when it -comes into contact with types like `!` or `Void`. +types, and its interaction with Rust's type layout invariants. The concept of +"uninitialized data" is extremely problematic when it comes into contact with +types like `!` or `Void`. For any given type, there may be valid and invalid bit-representations. For example, the type `u8` consists of a single byte and all possible bytes can be @@ -53,6 +55,18 @@ fn mem::uninitialized::() -> ! Yet calling this function does not diverge! It just breaks everything then eats your laundry instead. +This problem is most prominent with `!` but also applies to other types that +have restrictions on the values they can carry. For example, +`Some(mem::uninitialized::()).is_none()` could actually return `true` +because uninitialized memory could violate the invariant that a `bool` is always +`[00000000]` or `[00000001]` -- and Rust relies on this invariant when doing +enum layout. So, `mem::uninitialized::()` is instantaneous undefined +behavior just like `mem::uninitialized::()`. This also affects `mem::zeroed` +when considering types where the all-`0` bit pattern is not valid, like +references: `mem::zeroed::<&'static i32>()` is instantaneous undefined behavior. + +## Tracking uninitializedness in the type + An alternative way of representing uninitialized data is through a union type: ```rust @@ -63,14 +77,16 @@ union MaybeUninit { ``` Instead of creating an "uninitialized value", we can create a `MaybeUninit` -initialized with `uninit = ()`. Then, once we know that the value in the union +initialized with `uninit: ()`. Then, once we know that the value in the union is valid, we can extract it with `my_uninit.value`. This is a better way of handling uninitialized data because it doesn't involve lying to the type system and pretending that we have a value when we don't. It also better represents what's actually going on: we never *really* have a value of type `T` when we're using `uninitialized::`, what we have is some memory that contains either a value (`value: T`) or nothing (`uninit: ()`), with it being the programmer's -responsibility to keep track of which state we're in. +responsibility to keep track of which state we're in. Notice that creating a +`MaybeUninit` is safe for any `T`! Only when accessing `my_uninit.value`, +we have to be careful to ensure this has been properly initialized. To see how this can replace `uninitialized` and fix bugs in the process, consider the following code: @@ -161,6 +177,14 @@ impl MaybeUninit { } } + /// Create a new `MaybeUninit` in an uninitialized state, with the memory being + /// filled with `0` bytes. + pub fn zeroed() -> MaybeUninit { + let mut u = uninitialized(); + ptr::write_bytes(&mut u as *mut _, 0u8, 1); + u + } + /// Set the value of the `MaybeUninit`. The overwrites any previous value without dropping it. pub fn set(&mut self, val: T) -> &mut T { unsafe { From 4021b2c3353a557ca0a4f1f53fae699c27e3739d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 Aug 2018 16:36:37 +0200 Subject: [PATCH 14/19] clarify that as_(mut_)ptr can always be used to write; clarify Drop situation; change get to into_inner --- text/0000-uninitialized-uninhabited.md | 46 +++++++++++++++----------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 4bba022b3ff..c8953568ec9 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -162,7 +162,7 @@ Add the aforementioned `MaybeUninit` type to the standard library: #[repr(transparent)] union MaybeUninit { uninit: (), - value: T, + value: ManuallyDrop, } ``` @@ -171,6 +171,9 @@ The type should have at least the following interface ```rust impl MaybeUninit { /// Create a new `MaybeUninit` in an uninitialized state. + /// + /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. + /// It is your responsibility to make sure `T` gets dropped if it got initialized. pub fn uninitialized() -> MaybeUninit { MaybeUninit { uninit: (), @@ -178,7 +181,13 @@ impl MaybeUninit { } /// Create a new `MaybeUninit` in an uninitialized state, with the memory being - /// filled with `0` bytes. + /// filled with `0` bytes. It depends on `T` whether that already makes for + /// proper initialization. For example, `MaybeUninit::zeroed()` is initialized, + /// but `MaybeUninit<&'static i32>::zeroed()` is not because references must not + /// be null. + /// + /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. + /// It is your responsibility to make sure `T` gets dropped if it got initialized. pub fn zeroed() -> MaybeUninit { let mut u = uninitialized(); ptr::write_bytes(&mut u as *mut _, 0u8, 1); @@ -186,21 +195,20 @@ impl MaybeUninit { } /// Set the value of the `MaybeUninit`. The overwrites any previous value without dropping it. - pub fn set(&mut self, val: T) -> &mut T { + pub fn set(&mut self, val: T) { unsafe { - self.value = val; - &mut self.value + self.value = ManuallyDrop::new(val); } } - /// Take the value of the `MaybeUninit`, putting it into an uninitialized state. + /// Extract the value from the `MaybeUninit` container. /// /// # Unsafety /// /// It is up to the caller to guarantee that the the `MaybeUninit` really is in an initialized - /// state, otherwise undefined behaviour will result. - pub unsafe fn get(&self) -> T { - std::ptr::read(&self.value) + /// state, otherwise this will immediately cause undefined behavior. + pub unsafe fn into_inner(self) -> T { + std::ptr::read(&*self.value) } /// Get a reference to the contained value. @@ -208,9 +216,9 @@ impl MaybeUninit { /// # Unsafety /// /// It is up to the caller to guarantee that the the `MaybeUninit` really is in an initialized - /// state, otherwise undefined behaviour will result. + /// state, otherwise this will immediately cause undefined behavior. pub unsafe fn get_ref(&self) -> &T { - &self.value + &*self.value } /// Get a mutable reference to the contained value. @@ -218,21 +226,21 @@ impl MaybeUninit { /// # Unsafety /// /// It is up to the caller to guarantee that the the `MaybeUninit` really is in an initialized - /// state, otherwise undefined behaviour will result. + /// state, otherwise this will immediately cause undefined behavior. pub unsafe fn get_mut(&mut self) -> &mut T { - &mut self.value + &mut *self.value } - /// Get a pointer to the contained value. This pointer will only be valid if the `MaybeUninit` - /// is in an initialized state. + /// Get a pointer to the contained value. Reading from this pointer will be undefined + /// behavior unless the `MaybeUninit` is initialized. pub fn as_ptr(&self) -> *const T { - self as *const MaybeUninit as *const T + &*self.value *const T } - /// Get a mutable pointer to the contained value. This pointer will only be valid if the - /// `MaybeUninit` is in an initialized state. + /// Get a mutable pointer to the contained value. Reading from this pointer will be undefined + /// behavior unless the `MaybeUninit` is initialized. pub fn as_mut_ptr(&mut self) -> *mut T { - self as *mut MaybeUninit as *mut T + &mut *self.value *mut T } } ``` From e54882295fb8e5e84560b6a82a6f1f7b69b289ae Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 Aug 2018 17:08:25 +0200 Subject: [PATCH 15/19] remove repr(transparent) --- text/0000-uninitialized-uninhabited.md | 1 - 1 file changed, 1 deletion(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index c8953568ec9..290fc9518f5 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -159,7 +159,6 @@ library as a replacement. Add the aforementioned `MaybeUninit` type to the standard library: ```rust -#[repr(transparent)] union MaybeUninit { uninit: (), value: ManuallyDrop, From 8e087978dab39d22b44386d7d0afae646b6af4b2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 Aug 2018 17:09:26 +0200 Subject: [PATCH 16/19] use as_mut_ptr --- text/0000-uninitialized-uninhabited.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 290fc9518f5..26c55d71a00 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -189,7 +189,7 @@ impl MaybeUninit { /// It is your responsibility to make sure `T` gets dropped if it got initialized. pub fn zeroed() -> MaybeUninit { let mut u = uninitialized(); - ptr::write_bytes(&mut u as *mut _, 0u8, 1); + u.as_mut_ptr().write_bytes(0u8, 1); u } From b7df252780cb74ca82a1fdd1eafb21d0450fe9e9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 Aug 2018 17:57:11 +0200 Subject: [PATCH 17/19] fix some typos in the code and add playground link --- text/0000-uninitialized-uninhabited.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 26c55d71a00..42118609691 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -159,13 +159,14 @@ library as a replacement. Add the aforementioned `MaybeUninit` type to the standard library: ```rust -union MaybeUninit { +pub union MaybeUninit { uninit: (), value: ManuallyDrop, } ``` The type should have at least the following interface +([Playground link](https://play.rust-lang.org/?gist=81f5ab9a7e7107c9583de21382ef4333&version=nightly&mode=debug&edition=2015)): ```rust impl MaybeUninit { @@ -188,8 +189,8 @@ impl MaybeUninit { /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. /// It is your responsibility to make sure `T` gets dropped if it got initialized. pub fn zeroed() -> MaybeUninit { - let mut u = uninitialized(); - u.as_mut_ptr().write_bytes(0u8, 1); + let mut u = MaybeUninit::::uninitialized(); + unsafe { u.as_mut_ptr().write_bytes(0u8, 1); } u } @@ -233,13 +234,13 @@ impl MaybeUninit { /// Get a pointer to the contained value. Reading from this pointer will be undefined /// behavior unless the `MaybeUninit` is initialized. pub fn as_ptr(&self) -> *const T { - &*self.value *const T + unsafe { &*self.value as *const T } } /// Get a mutable pointer to the contained value. Reading from this pointer will be undefined /// behavior unless the `MaybeUninit` is initialized. pub fn as_mut_ptr(&mut self) -> *mut T { - &mut *self.value *mut T + unsafe { &mut *self.value as *mut T } } } ``` From 8ae636b95a5ba5f41541d23f92b3452bb5371395 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 Aug 2018 18:02:41 +0200 Subject: [PATCH 18/19] clarify into_inner and Drop --- text/0000-uninitialized-uninhabited.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-uninitialized-uninhabited.md b/text/0000-uninitialized-uninhabited.md index 42118609691..7170d24c5eb 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/0000-uninitialized-uninhabited.md @@ -201,7 +201,9 @@ impl MaybeUninit { } } - /// Extract the value from the `MaybeUninit` container. + /// Extract the value from the `MaybeUninit` container. This is a great way + /// to ensure that the data will get dropped, because the resulting `T` is + /// subject to the usual drop handling. /// /// # Unsafety /// From 1b0ef45b42bb361ab02a45259c42209ccd12ae4e Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 19 Aug 2018 12:19:34 +0200 Subject: [PATCH 19/19] RFC 1892 --- ...zed-uninhabited.md => 1892-uninitialized-uninhabited.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename text/{0000-uninitialized-uninhabited.md => 1892-uninitialized-uninhabited.md} (98%) diff --git a/text/0000-uninitialized-uninhabited.md b/text/1892-uninitialized-uninhabited.md similarity index 98% rename from text/0000-uninitialized-uninhabited.md rename to text/1892-uninitialized-uninhabited.md index 7170d24c5eb..134108191f4 100644 --- a/text/0000-uninitialized-uninhabited.md +++ b/text/1892-uninitialized-uninhabited.md @@ -1,7 +1,7 @@ -- Feature Name: uninitialized-uninhabited +- Feature Name: `uninitialized_uninhabited` - Start Date: 2017-02-09 -- RFC PR: (leave this empty) -- Rust Issue: (leave this empty) +- RFC PR: [rust-lang/rfcs#1892](https://github.com/rust-lang/rfcs/pull/1892) +- Rust Issue: [rust-lang/rust#53491](https://github.com/rust-lang/rust/issues/53491) # Summary [summary]: #summary