@@ -294,6 +294,166 @@ a `packed` type cannot transitively contain another `align`ed type.
294
294
> possible to [ safely create unaligned pointers to ` packed ` fields] [ 27060 ] .
295
295
> Like all ways to create undefined behavior in safe Rust, this is a bug.
296
296
297
+ ### The ` transparent ` representation
298
+
299
+ #### Background
300
+
301
+ It's sometimes useful to add additional type safety by introducing * newtypes* .
302
+ For example, code that handles numeric quantities in different units such as
303
+ millimeters, centimeters, grams, kilograms, etc. may want to use the type system
304
+ to rule out mistakes such as adding millimeters to grams:
305
+
306
+ ``` rust
307
+ use std :: ops :: Add ;
308
+
309
+ struct Millimeters (f64 );
310
+ struct Grams (f64 );
311
+
312
+ impl Add <Millimeters > for Millimeters {
313
+ type Output = Millimeters ;
314
+
315
+ fn add (self , other : Millimeters ) -> Millimeters {
316
+ Millimeters (self . 0 + other . 0 )
317
+ }
318
+ }
319
+
320
+ // Likewise: impl Add<Grams> for Grams {}
321
+ ```
322
+
323
+ Other uses of newtypes include using ` PhantomData ` to add lifetimes to raw
324
+ pointers or to implement the "phantom types" pattern. See the [ PhantomData]
325
+ documentation and [ the Nomicon] [ nomicon-phantom ] for more details.
326
+
327
+ The added type safety is especially useful when interacting with C or other
328
+ languages. However, in those cases we need to ensure the newtypes we add do not
329
+ introduce incompatibilities with the C ABI.
330
+
331
+ #### Newtypes in FFI
332
+
333
+ Luckily, ` repr(C) ` newtypes are laid out just like the type they wrap on all
334
+ platforms which Rust currently supports, and likely on many more. For example,
335
+ consider this C declaration:
336
+
337
+ ``` C
338
+ struct Object {
339
+ double weight; //< in grams
340
+ double height; //< in millimeters
341
+ // ...
342
+ }
343
+
344
+ void frobnicate (struct Object * );
345
+ ```
346
+
347
+ While using this C code from Rust, we could add `repr(C)` to the `Grams` and
348
+ `Millimeters` newtypes introduced above and use them to add some type safety
349
+ while staying compatible with the memory layout of `Object`:
350
+
351
+ ```rust,no_run
352
+ #[repr(C)]
353
+ struct Grams(f64);
354
+
355
+ #[repr(C)]
356
+ struct Millimeters(f64);
357
+
358
+ #[repr(C)]
359
+ struct Object {
360
+ weight: Grams,
361
+ height: Millimeters,
362
+ // ...
363
+ }
364
+
365
+ extern {
366
+ fn frobnicate(_: *mut Object);
367
+ }
368
+ ```
369
+
370
+ This works even when adding some ` PhantomData ` fields, because they are
371
+ zero-sized and therefore don't have to affect the memory layout.
372
+
373
+ However, there's more to the ABI than just memory layout: there's also the
374
+ question of how function call arguments and return values are passed. Many
375
+ common ABI treat a struct containing a single field differently from that field
376
+ itself, at least when the field is a scalar (e.g., integer or float or pointer).
377
+
378
+ To continue the above example, suppose the C library also exposes a function
379
+ like this:
380
+
381
+ ``` C
382
+ double calculate_weight (double height);
383
+ ```
384
+
385
+ Using our newtypes on the Rust side like this will cause an ABI mismatch on many
386
+ platforms:
387
+
388
+ ```rust,ignore
389
+ extern {
390
+ fn calculate_weight(height: Millimeters) -> Grams;
391
+ }
392
+ ```
393
+
394
+ For example, on x86_64 Linux, Rust will pass the argument in an integer
395
+ register, while the C function expects the argument to be in a floating-point
396
+ register. Likewise, the C function will return the result in a floating-point
397
+ register while Rust will expect it in an integer register.
398
+
399
+ Note that this problem is not specific to floats: To give another example,
400
+ 32-bit x86 linux will pass and return ` struct Foo(i32); ` on the stack while
401
+ ` i32 ` is placed in registers.
402
+
403
+ #### Enter ` repr(transparent) `
404
+
405
+ So while ` repr(C) ` happens to do the right thing with respect to memory layout,
406
+ it's not quite the right tool for newtypes in FFI. Instead of declaring a C
407
+ struct, we need to communicate to the Rust compiler that our newtype is just for
408
+ type safety on the Rust side. This is what ` repr(transparent) ` does.
409
+
410
+ The attribute can be applied to a newtype-like structs that contains a single
411
+ field. It indicates that the newtype should be represented exactly like that
412
+ field's type, i.e., the newtype should be ignored for ABI purpopses: not only is
413
+ it laid out the same in memory, it is also passed identically in function calls.
414
+
415
+ In the above example, the ABI mismatches can be prevented by making the newtypes
416
+ ` Grams ` and ` Millimeters ` transparent like this:
417
+
418
+ ``` rust
419
+ #[repr(transparent)]
420
+ struct Grams (f64 );
421
+
422
+ #[repr(transparent)]
423
+ struct Millimeters (f64 );
424
+ ```
425
+
426
+ In addition to that single field, any number of zero-sized fields are permitted,
427
+ including but not limited to ` PhantomData ` :
428
+
429
+ ``` rust
430
+ use std :: marker :: PhantomData ;
431
+
432
+ struct Foo { /* ... */ }
433
+
434
+ #[repr(transparent)]
435
+ struct FooPtrWithLifetime <'a >(* const Foo , PhantomData <& 'a Foo >);
436
+
437
+ #[repr(transparent)]
438
+ struct NumberWithUnit <T , U >(T , PhantomData <U >);
439
+
440
+ struct CustomZst ;
441
+
442
+ #[repr(transparent)]
443
+ struct PtrWithCustomZst <'a > {
444
+ ptr : FooPtrWithLifetime <'a >,
445
+ some_marker : CustomZst ,
446
+ }
447
+ ```
448
+
449
+ Transparent structs can be nested: ` PtrWithCustomZst ` is also represented
450
+ exactly like ` *const Foo ` .
451
+
452
+ Because ` repr(transparent) ` delegates all representation concerns to another
453
+ type, it is incompatible with all other ` repr(..) ` attributes. It also cannot be
454
+ applied to enums, unions, empty structs, structs whose fields are all
455
+ zero-sized, or structs with * multiple* non-zero-sized fields.
456
+
297
457
[ `align_of_val` ] : ../std/mem/fn.align_of_val.html
298
458
[ `size_of_val` ] : ../std/mem/fn.size_of_val.html
299
459
[ `align_of` ] : ../std/mem/fn.align_of.html
@@ -304,3 +464,5 @@ a `packed` type cannot transitively contain another `align`ed type.
304
464
[ zero-variant enumerations ] : items/enumerations.html#zero-variant-enums
305
465
[ undefined behavior ] : behavior-considered-undefined.html
306
466
[ 27060 ] : https://github.com/rust-lang/rust/issues/27060
467
+ [ PhantomData ] : https://doc.rust-lang.org/std/marker/struct.PhantomData.html
468
+ [ nomicon-phantom ] : https://doc.rust-lang.org/nomicon/phantom-data.html
0 commit comments