Skip to content

Commit 4b3335d

Browse files
authored
Merge pull request #82 from RalfJung/ffi
Don’t recommend empty enums for opaque types
2 parents 790e96b + b3d532f commit 4b3335d

File tree

1 file changed

+16
-5
lines changed

1 file changed

+16
-5
lines changed

src/ffi.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -737,11 +737,11 @@ void foo(struct Foo *arg);
737737
void bar(struct Bar *arg);
738738
```
739739
740-
To do this in Rust, let’s create our own opaque types with `enum`:
740+
To do this in Rust, let’s create our own opaque types:
741741
742742
```rust
743-
pub enum Foo {}
744-
pub enum Bar {}
743+
#[repr(C)] pub struct Foo { _private: [u8; 0] }
744+
#[repr(C)] pub struct Bar { _private: [u8; 0] }
745745
746746
extern "C" {
747747
pub fn foo(arg: *mut Foo);
@@ -750,7 +750,18 @@ extern "C" {
750750
# fn main() {}
751751
```
752752

753-
By using an `enum` with no variants, we create an opaque type that we can’t
754-
instantiate, as it has no variants. But because our `Foo` and `Bar` types are
753+
By including a private field and no constructor,
754+
we create an opaque type that we can't instantiate outside of this module.
755+
(A struct with no field could be instantiated by anyone.)
756+
We also want to use this type in FFI, so we have to add `#[repr(C)]`.
757+
And to avoid warning around using `()` in FFI, we instead use an empty array,
758+
which works just as well as an empty type but is FFI-compatible.
759+
760+
But because our `Foo` and `Bar` types are
755761
different, we’ll get type safety between the two of them, so we cannot
756762
accidentally pass a pointer to `Foo` to `bar()`.
763+
764+
Notice that it is a really bad idea to use an empty enum as FFI type.
765+
The compiler relies on empty enums being uninhabited, so handling values of type
766+
`&Empty` is a huge footgun and can lead to buggy program behavior (by triggering
767+
undefined behavior).

0 commit comments

Comments
 (0)