Rollup merge of #62514 - stephaneyfx:box-ffi, r=nikomatsakis
Clarify `Box<T>` representation and its use in FFI This officializes what was only shown as a code example in [the unsafe code guidelines](https://rust-lang.github.io/unsafe-code-guidelines/layout/function-pointers.html?highlight=box#use) and follows [the discussion](https://github.com/rust-lang/unsafe-code-guidelines/issues/157) in the corresponding repository. It is also related to [the issue](https://github.com/rust-lang/rust/issues/52976) regarding marking `Box<T>` `#[repr(transparent)]`. If the statement this PR adds is incorrect or a more in-depth discussion is warranted, I apologize. Should it be the case, the example in the unsafe code guidelines should be amended and some document should make it clear that it is not sound/supported.
This commit is contained in:
commit
9860a4eeb7
|
@ -61,7 +61,60 @@
|
|||
//! T` obtained from [`Box::<T>::into_raw`] may be deallocated using the
|
||||
//! [`Global`] allocator with [`Layout::for_value(&*value)`].
|
||||
//!
|
||||
//! So long as `T: Sized`, a `Box<T>` is guaranteed to be represented
|
||||
//! as a single pointer and is also ABI-compatible with C pointers
|
||||
//! (i.e. the C type `T*`). This means that if you have extern "C"
|
||||
//! Rust functions that will be called from C, you can define those
|
||||
//! Rust functions using `Box<T>` types, and use `T*` as corresponding
|
||||
//! type on the C side. As an example, consider this C header which
|
||||
//! declares functions that create and destroy some kind of `Foo`
|
||||
//! value:
|
||||
//!
|
||||
//! ```c
|
||||
//! /* C header */
|
||||
//!
|
||||
//! /* Returns ownership to the caller */
|
||||
//! struct Foo* foo_new(void);
|
||||
//!
|
||||
//! /* Takes ownership from the caller; no-op when invoked with NULL */
|
||||
//! void foo_delete(struct Foo*);
|
||||
//! ```
|
||||
//!
|
||||
//! These two functions might be implemented in Rust as follows. Here, the
|
||||
//! `struct Foo*` type from C is translated to `Box<Foo>`, which captures
|
||||
//! the ownership constraints. Note also that the nullable argument to
|
||||
//! `foo_delete` is represented in Rust as `Option<Box<Foo>>`, since `Box<Foo>`
|
||||
//! cannot be null.
|
||||
//!
|
||||
//! ```
|
||||
//! #[repr(C)]
|
||||
//! pub struct Foo;
|
||||
//!
|
||||
//! #[no_mangle]
|
||||
//! pub extern "C" fn foo_new() -> Box<Foo> {
|
||||
//! Box::new(Foo)
|
||||
//! }
|
||||
//!
|
||||
//! #[no_mangle]
|
||||
//! pub extern "C" fn foo_delete(_: Option<Box<Foo>>) {}
|
||||
//! ```
|
||||
//!
|
||||
//! Even though `Box<T>` has the same representation and C ABI as a C pointer,
|
||||
//! this does not mean that you can convert an arbitrary `T*` into a `Box<T>`
|
||||
//! and expect things to work. `Box<T>` values will always be fully aligned,
|
||||
//! non-null pointers. Moreover, the destructor for `Box<T>` will attempt to
|
||||
//! free the value with the global allocator. In general, the best practice
|
||||
//! is to only use `Box<T>` for pointers that originated from the global
|
||||
//! allocator.
|
||||
//!
|
||||
//! **Important.** At least at present, you should avoid using
|
||||
//! `Box<T>` types for functions that are defined in C but invoked
|
||||
//! from Rust. In those cases, you should directly mirror the C types
|
||||
//! as closely as possible. Using types like `Box<T>` where the C
|
||||
//! definition is just using `T*` can lead to undefined behavior, as
|
||||
//! described in [rust-lang/unsafe-code-guidelines#198][ucg#198].
|
||||
//!
|
||||
//! [ucg#198]: https://github.com/rust-lang/unsafe-code-guidelines/issues/198
|
||||
//! [dereferencing]: ../../std/ops/trait.Deref.html
|
||||
//! [`Box`]: struct.Box.html
|
||||
//! [`Box<T>`]: struct.Box.html
|
||||
|
|
Loading…
Reference in New Issue