Document subtleties of ManuallyDrop · qinheping/verify-rust-std@6695a56 (original) (raw)

1

1

`use crate::ops::{Deref, DerefMut, DerefPure};

`

2

2

`use crate::ptr;

`

3

3

``

4

``

`` -

/// A wrapper to inhibit the compiler from automatically calling T’s destructor.

``

5

``

`-

/// This wrapper is 0-cost.

`

``

4

`` +

/// A wrapper to inhibit the compiler from automatically calling T’s

``

``

5

`+

/// destructor. This wrapper is 0-cost.

`

6

6

`///

`

7

7

`` /// ManuallyDrop<T> is guaranteed to have the same layout and bit validity as

``

8

``

`` -

/// T, and is subject to the same layout optimizations as T. As a consequence,

``

9

``

`-

/// it has no effect on the assumptions that the compiler makes about its

`

10

``

`` -

/// contents. For example, initializing a ManuallyDrop<&mut T> with [mem::zeroed]

``

11

``

`-

/// is undefined behavior. If you need to handle uninitialized data, use

`

12

``

`` -

/// [MaybeUninit<T>] instead.

``

``

8

`` +

/// T, and is subject to the same layout optimizations as T. As a

``

``

9

`+

/// consequence, it has no effect on the assumptions that the compiler makes

`

``

10

`` +

/// about its contents. For example, initializing a ManuallyDrop<&mut T> with

``

``

11

`` +

/// [mem::zeroed] is undefined behavior. If you need to handle uninitialized

``

``

12

`` +

/// data, use [MaybeUninit<T>] instead.

``

13

13

`///

`

14

``

`` -

/// Note that accessing the value inside a ManuallyDrop<T> is safe.

``

15

``

`` -

/// This means that a ManuallyDrop<T> whose content has been dropped must not

``

16

``

`-

/// be exposed through a public safe API.

`

17

``

`` -

/// Correspondingly, ManuallyDrop::drop is unsafe.

``

``

14

`` +

/// Note that accessing the value inside a ManuallyDrop<T> is safe. This means

``

``

15

`` +

/// that a ManuallyDrop<T> whose content has been dropped must not be exposed

``

``

16

`` +

/// through a public safe API. Correspondingly, ManuallyDrop::drop is unsafe.

``

18

17

`///

`

19

18

`` /// # ManuallyDrop and drop order.

``

20

19

`///

`

`@@ -40,9 +39,114 @@ use crate::ptr;

`

40

39

`/// }

`

41

40

```` /// ```


`42`

`41`

`///

`

``

`42`

`` +

/// # Interaction with `Box`.

``

``

`43`

`+

///

`

``

`44`

`` +

/// Currently, once the `Box<T>` inside a `ManuallyDrop<Box<T>>` is dropped,

``

``

`45`

`` +

/// moving the `ManuallyDrop<Box<T>>` is [considered to be undefined

``

``

`46`

`+

/// behavior](https://github.com/rust-lang/unsafe-code-guidelines/issues/245).

`

``

`47`

`+

/// That is, the following code causes undefined behavior:

`

``

`48`

`+

///

`

``

`49`

```` +

/// ```no_run

``

50

`+

/// use std::mem::ManuallyDrop;

`

``

51

`+

///

`

``

52

`+

/// let mut x = ManuallyDrop::new(Box::new(42));

`

``

53

`+

/// unsafe {

`

``

54

`+

/// ManuallyDrop::drop(&mut x);

`

``

55

`+

/// }

`

``

56

`+

/// let y = x; // Undefined behavior!

`

``

57


/// ```

``

58

`+

///

`

``

59

`+

/// This may change in the future. In the meantime, consider using

`

``

60

`` +

/// [MaybeUninit] instead.

``

``

61

`+

///

`

``

62

`` +

/// # Safety hazards when storing ManuallyDrop in a struct / enum.

``

``

63

`+

///

`

``

64

`+

/// Special care is needed when all of the conditions below are met:

`

``

65

`` +

/// * A field of a struct or enum is a ManuallyDrop or contains a

``

``

66

`` +

/// ManuallyDrop, without the ManuallyDrop being inside a union.

``

``

67

`+

/// * The struct or enum is part of public API, or is stored in a struct or enum

`

``

68

`+

/// that is part of public API.

`

``

69

`` +

/// * There is code outside of a Drop implementation that calls

``

``

70

`` +

/// [ManuallyDrop::drop] or [ManuallyDrop::take] on the ManuallyDrop

``

``

71

`+

/// field.

`

``

72

`+

///

`

``

73

`+

/// In particular, the following hazards can occur:

`

``

74

`+

///

`

``

75

`+

/// #### Storing generic types

`

``

76

`+

///

`

``

77

`` +

/// If the ManuallyDrop contains a client-supplied generic type, the client

``

``

78

`` +

/// might provide a Box, causing undefined behavior when the struct / enum is

``

``

79

`+

/// later moved, as mentioned above. For example, the following code causes

`

``

80

`+

/// undefined behavior:

`

``

81

`+

///

`

``

82


/// ```no_run

``

83

`+

/// use std::mem::ManuallyDrop;

`

``

84

`+

///

`

``

85

`+

/// pub struct BadOption {

`

``

86

`` +

/// // Invariant: Has been dropped iff is_some is false.

``

``

87

`+

/// value: ManuallyDrop,

`

``

88

`+

/// is_some: bool,

`

``

89

`+

/// }

`

``

90

`+

/// impl BadOption {

`

``

91

`+

/// pub fn new(value: T) -> Self {

`

``

92

`+

/// Self { value: ManuallyDrop::new(value), is_some: true }

`

``

93

`+

/// }

`

``

94

`+

/// pub fn change_to_none(&mut self) {

`

``

95

`+

/// if self.is_some {

`

``

96

`+

/// self.is_some = false;

`

``

97

`+

/// unsafe {

`

``

98

`` +

/// // SAFETY: value hasn't been dropped yet, as per the invariant

``

``

99

`+

/// // (This is actually unsound!)

`

``

100

`+

/// ManuallyDrop::drop(&mut self.value);

`

``

101

`+

/// }

`

``

102

`+

/// }

`

``

103

`+

/// }

`

``

104

`+

/// }

`

``

105

`+

///

`

``

106

`+

/// // In another crate:

`

``

107

`+

///

`

``

108

`+

/// let mut option = BadOption::new(Box::new(42));

`

``

109

`+

/// option.change_to_none();

`

``

110

`+

/// let option2 = option; // Undefined behavior!

`

``

111


/// ```

``

112

`+

///

`

``

113

`+

/// #### Deriving traits

`

``

114

`+

///

`

``

115

`` +

/// Deriving Debug, Clone, PartialEq, PartialOrd, Ord, or Hash on

``

``

116

`+

/// the struct / enum could be unsound, since the derived implementations of

`

``

117

`` +

/// these traits would access the ManuallyDrop field. For example, the

``

``

118

`+

/// following code causes undefined behavior:

`

``

119

`+

///

`

``

120


/// ```no_run

``

121

`+

/// use std::mem::ManuallyDrop;

`

``

122

`+

///

`

``

123

`+

/// #[derive(Debug)] // This is unsound!

`

``

124

`+

/// pub struct Foo {

`

``

125

`+

/// value: ManuallyDrop,

`

``

126

`+

/// }

`

``

127

`+

/// impl Foo {

`

``

128

`+

/// pub fn new() -> Self {

`

``

129

`+

/// let mut temp = Self {

`

``

130

`+

/// value: ManuallyDrop::new(String::from("Unsafe rust is hard"))

`

``

131

`+

/// };

`

``

132

`+

/// unsafe {

`

``

133

`` +

/// // SAFETY: value hasn't been dropped yet.

``

``

134

`+

/// ManuallyDrop::drop(&mut temp.value);

`

``

135

`+

/// }

`

``

136

`+

/// temp

`

``

137

`+

/// }

`

``

138

`+

/// }

`

``

139

`+

///

`

``

140

`+

/// // In another crate:

`

``

141

`+

///

`

``

142

`+

/// let foo = Foo::new();

`

``

143

`+

/// println!("{:?}", foo); // Undefined behavior!

`

``

144


/// ```

``

145

`+

///

`

43

146

`/// [drop order]: https://doc.rust-lang.org/reference/destructors.html

`

44

147

`` /// [mem::zeroed]: crate::mem::zeroed

``

45

148

`` /// [MaybeUninit<T>]: crate::mem::MaybeUninit

``

``

149

`` +

/// [MaybeUninit]: crate::mem::MaybeUninit

``

46

150

`#[stable(feature = "manually_drop", since = "1.20.0")]

`

47

151

`#[lang = "manually_drop"]

`

48

152

`#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]

`