]> git.proxmox.com Git - rustc.git/blob - src/doc/unstable-book/src/language-features/repr-transparent.md
New upstream version 1.27.2+dfsg1
[rustc.git] / src / doc / unstable-book / src / language-features / repr-transparent.md
1 # `repr_transparent`
2
3 The tracking issue for this feature is: [#43036]
4
5 [#43036]: https://github.com/rust-lang/rust/issues/43036
6
7 ------------------------
8
9 This feature enables the `repr(transparent)` attribute on structs, which enables
10 the use of newtypes without the usual ABI implications of wrapping the value in
11 a struct.
12
13 ## Background
14
15 It's sometimes useful to add additional type safety by introducing *newtypes*.
16 For example, code that handles numeric quantities in different units such as
17 millimeters, centimeters, grams, kilograms, etc. may want to use the type system
18 to rule out mistakes such as adding millimeters to grams:
19
20 ```rust
21 use std::ops::Add;
22
23 struct Millimeters(f64);
24 struct Grams(f64);
25
26 impl Add<Millimeters> for Millimeters {
27 type Output = Millimeters;
28
29 fn add(self, other: Millimeters) -> Millimeters {
30 Millimeters(self.0 + other.0)
31 }
32 }
33
34 // Likewise: impl Add<Grams> for Grams {}
35 ```
36
37 Other uses of newtypes include using `PhantomData` to add lifetimes to raw
38 pointers or to implement the "phantom types" pattern. See the [PhantomData]
39 documentation and [the Nomicon][nomicon-phantom] for more details.
40
41 The added type safety is especially useful when interacting with C or other
42 languages. However, in those cases we need to ensure the newtypes we add do not
43 introduce incompatibilities with the C ABI.
44
45 ## Newtypes in FFI
46
47 Luckily, `repr(C)` newtypes are laid out just like the type they wrap on all
48 platforms which Rust currently supports, and likely on many more. For example,
49 consider this C declaration:
50
51 ```C
52 struct Object {
53 double weight; //< in grams
54 double height; //< in millimeters
55 // ...
56 }
57
58 void frobnicate(struct Object *);
59 ```
60
61 While using this C code from Rust, we could add `repr(C)` to the `Grams` and
62 `Millimeters` newtypes introduced above and use them to add some type safety
63 while staying compatible with the memory layout of `Object`:
64
65 ```rust,no_run
66 #[repr(C)]
67 struct Grams(f64);
68
69 #[repr(C)]
70 struct Millimeters(f64);
71
72 #[repr(C)]
73 struct Object {
74 weight: Grams,
75 height: Millimeters,
76 // ...
77 }
78
79 extern {
80 fn frobnicate(_: *mut Object);
81 }
82 ```
83
84 This works even when adding some `PhantomData` fields, because they are
85 zero-sized and therefore don't have to affect the memory layout.
86
87 However, there's more to the ABI than just memory layout: there's also the
88 question of how function call arguments and return values are passed. Many
89 common ABI treat a struct containing a single field differently from that field
90 itself, at least when the field is a scalar (e.g., integer or float or pointer).
91
92 To continue the above example, suppose the C library also exposes a function
93 like this:
94
95 ```C
96 double calculate_weight(double height);
97 ```
98
99 Using our newtypes on the Rust side like this will cause an ABI mismatch on many
100 platforms:
101
102 ```rust,ignore
103 extern {
104 fn calculate_weight(height: Millimeters) -> Grams;
105 }
106 ```
107
108 For example, on x86_64 Linux, Rust will pass the argument in an integer
109 register, while the C function expects the argument to be in a floating-point
110 register. Likewise, the C function will return the result in a floating-point
111 register while Rust will expect it in an integer register.
112
113 Note that this problem is not specific to floats: To give another example,
114 32-bit x86 linux will pass and return `struct Foo(i32);` on the stack while
115 `i32` is placed in registers.
116
117 ## Enter `repr(transparent)`
118
119 So while `repr(C)` happens to do the right thing with respect to memory layout,
120 it's not quite the right tool for newtypes in FFI. Instead of declaring a C
121 struct, we need to communicate to the Rust compiler that our newtype is just for
122 type safety on the Rust side. This is what `repr(transparent)` does.
123
124 The attribute can be applied to a newtype-like structs that contains a single
125 field. It indicates that the newtype should be represented exactly like that
126 field's type, i.e., the newtype should be ignored for ABI purpopses: not only is
127 it laid out the same in memory, it is also passed identically in function calls.
128
129 In the above example, the ABI mismatches can be prevented by making the newtypes
130 `Grams` and `Millimeters` transparent like this:
131
132 ```rust
133 #![feature(repr_transparent)]
134
135 #[repr(transparent)]
136 struct Grams(f64);
137
138 #[repr(transparent)]
139 struct Millimeters(f64);
140 ```
141
142 In addition to that single field, any number of zero-sized fields are permitted,
143 including but not limited to `PhantomData`:
144
145 ```rust
146 #![feature(repr_transparent)]
147
148 use std::marker::PhantomData;
149
150 struct Foo { /* ... */ }
151
152 #[repr(transparent)]
153 struct FooPtrWithLifetime<'a>(*const Foo, PhantomData<&'a Foo>);
154
155 #[repr(transparent)]
156 struct NumberWithUnit<T, U>(T, PhantomData<U>);
157
158 struct CustomZst;
159
160 #[repr(transparent)]
161 struct PtrWithCustomZst<'a> {
162 ptr: FooPtrWithLifetime<'a>,
163 some_marker: CustomZst,
164 }
165 ```
166
167 Transparent structs can be nested: `PtrWithCustomZst` is also represented
168 exactly like `*const Foo`.
169
170 Because `repr(transparent)` delegates all representation concerns to another
171 type, it is incompatible with all other `repr(..)` attributes. It also cannot be
172 applied to enums, unions, empty structs, structs whose fields are all
173 zero-sized, or structs with *multiple* non-zero-sized fields.
174
175 [PhantomData]: https://doc.rust-lang.org/std/marker/struct.PhantomData.html
176 [nomicon-phantom]: https://doc.rust-lang.org/nomicon/phantom-data.html