Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Traits

Every emitted struct comes with a fixed set of trait implementations. Some are derived, some are written out; which is which depends on the model. Enums derive Clone, Copy, PartialEq, Debug, Default in both models, and unions derive Copy, Clone in the unsafe model regardless of their fields; the table below is for structs.

TraitUnsafe modelRefcount model
Copyderived when every field is copyablenever
Clonederivedderived when member-wise, else hand-written
Defaultderived when possible, else hand-writtensame
Dropnot emitted (#310)hand-written from a user destructor with a body
Ord, PartialOrd, PartialEq, Eqhand-written from operator<same
ByteReprnot neededhand-written for every record and enum
Recordnot neededderived for every struct

Copy and Clone

In the unsafe model a record derives Copy unless a field is translated to a Vec, BTreeMap, Option<Box<T>> (std::unique_ptr), or a record that is not itself Copy; Clone is derived unless the C++ copy constructor is deleted. The refcount model skips Clone altogether when the copy constructor is deleted, and never derives Copy. It derives Clone for C structs and for classes with an implicit or defaulted copy constructor, which copy each field with its own clone, i.e., its C++ copy constructor. The exceptions are fields that are or nest a Value, such as std::vector<int> (Value<Vec<i32>>, see Boxing) or std::vector<std::vector<int>> (Value<Vec<Value<Vec<i32>>>>), whose derived clone would share the Values instead of copying them; the generated Clone then translates the implicit copy constructor, which copies them deeply.

A user-defined copy constructor takes a Ptr to the source, while clone only has &self. clone passes it a pointer to a shallow copy of self, built field by field without running any copy constructor, so that the source is copied exactly once.

This is what gives struct assignment and pass-by-value C++’s member-by-member copy.

Default

Default is the value of a T x; without initializer, of T x = {}, and of the elements of new T[n]. It is derived when the derived impl gives the C zero value, and hand-written otherwise: when the class has a user-defined default constructor, default() calls it; when a field is a C array, a std::array, a function pointer, or a libc record, default() builds the struct field by field, each with the same default value the converter uses for a variable of that type declared without an initializer:

#![allow(unused)]
fn main() {
impl Default for S {
    fn default() -> Self {
        S {
            head: 0_i32,
            tail: [0_i32; 3],
            buf: [0 as libc::c_char; 4],
        }
    }
}
}

Unions always get a hand-written impl that zeroes their bytes.

Drop

A user-defined destructor with a non-empty body becomes impl Drop, with the body translated as a method body. Only the refcount model emits it; the unsafe model drops destructors silently (#310).

Comparison

A class that defines operator< (as a method or an out-of-line function) gets Ord, PartialOrd, PartialEq, and Eq, all expressed through the emitted lt method: cmp calls it both ways to pick Less, Greater, or Equal, and eq is “neither is less”. Only one comparison operator per class is supported, and only operator<. The converter assumes the operator is const, which Rust requires (cmp and eq take &self) but C++ does not; a non-const operator< is still emitted as lt(&self, ...).

ByteRepr

The refcount model emits ByteRepr for every record and enum: byte_size, to_bytes, and from_bytes laid out with the C offsets of the fields (enums go through their i32 value). It is what lets a Ptr to the type be reinterpreted as bytes, and bytes be read back as the type. A record with a field that has no byte representation gets an impl with byte_size alone, which locates the elements of arrays of the record for field pointers, and reinterpreting it panics at run time.

Record

#[derive(Record)], from libcc2rs-macros, gives pointers to fields access to the fields of a struct. The converter writes the C offset of each field, from Clang’s record layout, as an #[offset(N)] attribute on it, and the derive generates the table of offsets that field_ptr! looks fields up in, and the locate functions that find a field from its offset when a field pointer is accessed.