Classes and Structs
A class becomes a struct with one field per data member, an impl block holding
its constructors and methods, and trait implementations after it. Given
class Counter {
int count_;
public:
Counter(int start) : count_(start) {}
~Counter() { count_ = 0; }
int get() const { return count_; }
void set(int v) { count_ = v; }
};
the unsafe model produces
#![allow(unused)]
fn main() {
#[repr(C)]
#[derive(Copy, Clone, Default)]
pub struct Counter {
count_: i32,
}
impl Counter {
pub unsafe fn Counter(mut start: i32) -> Self {
let mut this = Self { count_: start };
this
}
pub unsafe fn get(&self) -> i32 {
return self.count_;
}
pub unsafe fn set(&mut self, mut v: i32) {
self.count_ = v;
}
}
}
and the refcount model produces
#![allow(unused)]
fn main() {
#[derive(Default)]
pub struct Counter {
count_: Value<i32>,
}
impl Counter {
pub fn Counter(start: i32) -> Self {
let start: Value<i32> = Rc::new(RefCell::new(start));
let mut this = Self {
count_: Rc::new(RefCell::new(*start.borrow())),
};
this
}
pub fn get(&self) -> i32 {
return *self.count_.borrow();
}
pub fn set(&self, v: i32) {
let v: Value<i32> = Rc::new(RefCell::new(v));
*self.count_.borrow_mut() = *v.borrow();
}
}
impl Drop for Counter {
fn drop(&mut self) {
*self.count_.borrow_mut() = 0;
}
}
impl Clone for Counter {
fn clone(&self) -> Self {
let mut this = Self {
count_: Rc::new(RefCell::new(*self.count_.borrow())),
};
this
}
}
impl ByteRepr for Counter { /* byte_size, to_bytes, from_bytes */ }
}
The unsafe model adds #[repr(C)] and derives what it can; the refcount model
writes most impls by hand. Which traits are emitted, and when they are derived
rather than written, is on the Traits page.
Fields keep their C++ access: pub for public members, nothing for private
ones. A class nested in another class is emitted as its own top-level struct,
named Outer_Inner (see Naming); Rust has no nested types, and
the outer struct refers to it by that name. A record that is only
forward-declared, or whose definition is never converted because it is only used
behind pointers, is emitted at the end of the file as an empty
pub struct Name; (see The Translation Pipeline).
A constructor becomes an associated function named after the class. It opens
with let mut this = Self { ... }, one field per member initializer, then runs
the C++ body and returns this. Copy and move constructors become copy_from
and move_from (see Naming); the Clone impl calls copy_from
when the copy constructor is user-defined.
Methods take &self when const and &mut self otherwise in the unsafe model.
In the refcount model they always take &self, since mutation goes through the
fields’ RefCells. Inside a method, this is self.
A destructor with a body becomes impl Drop in the refcount model.
Warning
The unsafe model does not emit destructors at all; a user-defined destructor is silently dropped (#310).
Inheritance
An abstract class becomes a trait with one method per pure virtual function, and a class deriving from it implements the trait with its overrides. Given
class Animal {
public:
virtual bool bark() const = 0;
};
class Dog : public Animal {
bool bark() const override { return true; }
};
the unsafe model produces (attributes omitted)
#![allow(unused)]
fn main() {
pub unsafe trait Animal {
unsafe fn bark(&self) -> bool;
}
pub struct Dog {}
unsafe impl Animal for Dog {
unsafe fn bark(&self) -> bool {
return true;
}
}
}
and the refcount model produces (attributes and the Clone and ByteRepr impls
omitted)
#![allow(unused)]
fn main() {
pub trait Animal {
fn bark(&self) -> bool;
}
pub struct Dog {}
impl Animal for Dog {
fn bark(&self) -> bool {
return true;
}
}
}
Non-virtual methods of the derived class go into its own impl Dog block as
usual. Because the base is a trait, pointers to it are *mut dyn Animal in the
unsafe model and PtrDyn<dyn Animal> in the
refcount model, and a Dog * is upcast at the call site. Only the first base
class is considered, and only virtual methods go through the trait; bases with
data members or non-virtual methods, and multiple inheritance, are outside the
supported subset.
Templates
Class templates are translated by full instantiation: each instantiation used by
the program becomes its own struct and impl block, named after the template
arguments (see Naming). MyContainer<int> and
MyContainer<char> become MyContainer_int_ and MyContainer_char_, each with
a complete copy of the methods specialized for its element type. Nothing is
shared between instantiations, and Rust generics are not used.
Flexible array members
A trailing array member of size 0, 1, or [] that C code over-indexes into
memory allocated past the struct is detected with clang’s
isFlexibleArrayMemberLike. In the unsafe model an access to such a member is
not an array index, which Rust would bounds-check against the declared length,
but pointer arithmetic from the array’s start: s.bytes[i] becomes
*s.bytes.as_mut_ptr().add(i as usize), and &s.bytes[i] the same without the
leading *. The refcount model has no dedicated handling; the pattern works
when the array is a union member, because the union accessor returns a Ptr
over the whole allocation that can be offset freely.