1.0.0[−][src]Module nom::lib::std::boxed
A pointer type for heap allocation.
Box<T>
, casually referred to as a 'box', provides the simplest form of
heap allocation in Rust. Boxes provide ownership for this allocation, and
drop their contents when they go out of scope.
Examples
Move a value from the stack to the heap by creating a Box
:
let val: u8 = 5; let boxed: Box<u8> = Box::new(val);
Move a value from a Box
back to the stack by dereferencing:
let boxed: Box<u8> = Box::new(5); let val: u8 = *boxed;
Creating a recursive data structure:
#[derive(Debug)] enum List<T> { Cons(T, Box<List<T>>), Nil, } let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil)))); println!("{:?}", list);
This will print Cons(1, Cons(2, Nil))
.
Recursive structures must be boxed, because if the definition of Cons
looked like this:
Cons(T, List<T>),
It wouldn't work. This is because the size of a List
depends on how many
elements are in the list, and so we don't know how much memory to allocate
for a Cons
. By introducing a Box<T>
, which has a defined size, we know how
big Cons
needs to be.
Memory layout
For non-zero-sized values, a Box
will use the Global
allocator for
its allocation. It is valid to convert both ways between a Box
and a
raw pointer allocated with the Global
allocator, given that the
Layout
used with the allocator is correct for the type. More precisely,
a value: *mut T
that has been allocated with the Global
allocator
with Layout::for_value(&*value)
may be converted into a box using
Box::<T>::from_raw(value)
. Conversely, the memory backing a value: *mut 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 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.
Structs
Box | A pointer type for heap allocation. |