@@ -737,11 +737,11 @@ void foo(struct Foo *arg);
737
737
void bar(struct Bar * arg);
738
738
```
739
739
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:
741
741
742
742
```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] }
745
745
746
746
extern "C" {
747
747
pub fn foo(arg: *mut Foo);
@@ -750,7 +750,18 @@ extern "C" {
750
750
# fn main() {}
751
751
```
752
752
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
755
761
different, we’ll get type safety between the two of them, so we cannot
756
762
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