In C++, the term POD type is used to describe types which only contain plain old data. Quoted from here:
A class [...] without constructors, destructors and virtual members functions.
Is there a similar concept in Rust?
Quick answer; C++'s POD types ≈ Rust's Copy
types.
However, there are a few other, related concepts. Let's discuss them in detail!
The Copy
trait is closest to the term "POD type" in C++ and is defined as follows:
Types whose values can be duplicated simply by copying bits.
By "copying bits", the documentation basically means memcpy()
. This includes all primitive types, such as u32
, f32
, char
and bool
, but user defined types can be Copy
, too. Usually the trait is simply derived:
#[derive(Clone, Copy)]
struct Point {
x: f32,
y: f32,
}
You might have noticed the Clone
: the Clone
trait is a requirement of Copy
and is defined as:
A common trait for the ability to explicitly duplicate an object.
Clone
says that a type is "somehow able to duplicate itself" and Copy
requires more by saying the type is "able to duplicate itself by just copying the type's bits".
The C++ answer states that POD types don't contain "constructors, destructors and virtual members functions". Let's break that down for Rust:
Constructors: Rust doesn't have dedicated constructor methods, but instead uses associated functions (static methods in C++). Every type, even all Copy
types, may have as many associated functions and methods as they want. In Rust it is not really a requirement to be "POD". In particular, even Rust's primitive types have many methods, like u32::pow()
. This restriction doesn't apply to Rust.
Destructors: in Rust, objects are destructed by calling drop()
from the Drop
trait (or rather: drop()
is automatically called at the end of scope). Types can't be Copy
when they implement the Drop
trait! Rust has a similar restriction here.
Virtual member functions: in Rust, virtual is not a property of a function. Most functions can be used in a virtual context, that is: they can be used with dynamic dispatch, but being Copy
doesn't prevent a type from being used in a dynamic dispatch context (in Rust terms: being used as trait object). This is partly thanks to the fact that the vptr is not stored within the type, but next to the pointer to the object (fat pointers). This point doesn't apply to Rust.
However, you might require more things from your "POD types" than "being copyable by copying bits". Rust has built-in, specific trait bounds for some additional properties:
'static
: requires that the type does not contain any internal references. For example, struct Foo<'a>(&'a u32);
would not satisfy this trait bound. If you know your type is 'static
, you have no lifetime restrictions and the borrow checker won't make you any problems.
Sized
: requires that the type has a size known at compile time and can thus be stored on the stack. That is true for almost all types; there are only a few rare-ish exceptions: [T]
and str
(slices), dyn MyTrait
(trait objects) and structs that contain an unsized types as the last field (e.g. Path
).
Send
and Sync
: require that the type can be sent to other threads and can shared via immutable reference (&T
) across threads. Both traits are implemented for almost all types. It's not implemented if the type contains some kind of magic (e.g. interior mutability, references to something without having a lifetime, ...). Requiring those two trait bounds makes sure that your type does not have this magic.
So if you really want a very simple, no-magic "bag of bits" type, this seems like a fitting trait/collection of bounds:
trait Pod: 'static + Copy + Sized + Send + Sync {}
impl<T: 'static + Copy + Sized + Send + Sync> Pod for T {}
With those kinds of type, you won't deal with move semantics (due to Copy
) or the borrow checker (due to 'static
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With