vptr 0.1.0

Light references to trait objects by embedding the virtual table pointer in the struct
Documentation
# vptr

Enable light references to trait

## Intro

### What are trait object and virtual table ?

In rust, you can have dynamic dispatch with the so-called Trait object.
Here is a typical example

```rust
trait Shape { fn area(&self) -> f32; }
struct Rectangle { w: f32, h : f32 }
impl Shape for Rectangle { fn area(&self) -> f32 { self.w * self.h } }
struct Circle { r: f32 }
impl Shape for Circle { fn area(&self) -> f32 { 3.14 * self.r * self.r } }

// Given an array of Shape, compute the sum of their area
fn total_area(list: &[&dyn Shape]) -> f32 {
    list.iter().map(|x| x.area()).fold(0., |a, b| a+b)
}
```
In this example the function `total_area` takes a reference of trait objects that implement
the `Shape` trait. Internally, this `&dyn Shape` reference is composed of two pointer:
a pointer to the object, and a pointer to a virtual table. The virtual table is a static
structure containing the function pointer to the `area` function. Such virtual table exist
for each type that implements the trait, but each instance of the same type share the same
virtual table. Having only a pointer to the struct itself would not be enough as the
`total_area` does not know the exact type of what it is pointed to, so it would not know from
which `impl` to call the `area` function.

This box diagram shows a simplified representation of the memory layout

```ascii
   &dyn Shape      ╭──────> Rectangle     ╭─> vtable of Shape for Rectangle
 ┏━━━━━━━━━━━━━┓   │       ┏━━━━━━━━━┓    │        ┏━━━━━━━━━┓
 ┃ data        ┠───╯       ┃ w       ┃    │        ┃ area()  ┃
 ┣━━━━━━━━━━━━━┫           ┣━━━━━━━━━┫    │        ┣━━━━━━━━━┫
 ┃ vtable ptr  ┠─────╮     ┃ h       ┃    │        ┃ drop()  ┃
 ┗━━━━━━━━━━━━━┛     │     ┗━━━━━━━━━┛    │        ┣━━━━━━━━━┫
                     ╰────────────────────╯        ┃ size    ┃
                                                   ╏         ╏
```

Other languages such as C++ implements that differently: in C++, each instance of a dynamic class
has a pointer to the virtual table, inside of the class. So just a normal pointer to the base class
is enough to do dynamic dispatch

Both approaches have pros and cons: in Rust, the object themselves are a bit smaller as they
do not have a pointer to the virtual table. They can also implement trait from other crates
which would not work in C++ as it would have to somehow put the pointer to the virtual table
inside the object. But rust pointer to trait are twice as big as normal pointer. Which is
usually not a problem. Unless of course you want to pack many trait object reference in a vector
in constrained memory, or pass them through ffi to C function that only handle pointer as data.
That's where this crate comes in!

### Light references

This crates allows to easily opt in to light references to trait for a type, by having
pointers to the virtual table within the object.

```rust
trait Shape { fn area(&self) -> f32; }
#[vptr(Shape)]
struct Rectangle { w: f32, h : f32 }
impl Shape for Rectangle { fn area(&self) -> f32 { self.w * self.h } }
#[vptr(Shape)]
struct Circle { r: f32 }
impl Shape for Circle { fn area(&self) -> f32 { 3.14 * self.r * self.r } }

// Given an array of Shape, compute the sum of their area
fn total_area(list: &[LightRef<dyn Shape>]) -> f32 {
    list.iter().map(|x| x.area()).fold(0., |a, b| a+b)
}
```

Same as before, but we added `#[vptr(Shape)]` and are now using `LightRef<Shape>` instead of
`&dyn Shame`.  The difference is that the LightRef has only the size of one pointer


```ascii
 LightRef<Shape>       Rectangle          ╭─>VTableData  ╭─>vtable of Shape for Rectangle
 ┏━━━━━━━━━━━━━┓      ┏━━━━━━━━━━━━┓ ╮    │  ┏━━━━━━━━┓  │     ┏━━━━━━━━━┓
 ┃ ptr         ┠──╮   ┃ w          ┃ │ ╭──│──┨ offset ┃  │     ┃ area()  ┃
 ┗━━━━━━━━━━━━━┛  │   ┣━━━━━━━━━━━━┫ ⎬─╯  │  ┣━━━━━━━━┫  │     ┣━━━━━━━━━┫
                  │   ┃ h          ┃ │    │  ┃ vtable ┠──╯     ┃ drop()  ┃
                  │   ┣━━━━━━━━━━━━┫ ╯    │  ┗━━━━━━━━┛        ┣━━━━━━━━━┫
                  ╰──>┃ vptr_Shape ┠──────╯                    ┃ size    ┃
                      ┗━━━━━━━━━━━━┛                           ╏         ╏
```


## The `#[vptr]` macro

The `#[vptr(Trait)]` macro can be applied to a struct and it adds members to the struct
with pointer to the vtable, these members are of type VPtr<S, Trait>, where S is the struct.
The macro also implements the `HasVPtr` trait which allow the creation of `LightRef` for this

You probably want to derive from `Default`, otherwise, the extra fields needs to be initialized
manually (with `Default::default()` or `VPtr::new()`)

```rust
trait Shape { fn area(&self) -> f32; }
#[vptr(Shape, ToString)] // There can be several traits
#[derive(Default)]
struct Rectangle { w: f32, h : f32 }

// The traits within #[vptr(...)] need to be implemented for that type
impl Shape for Rectangle { fn area(&self) -> f32 { self.w * self.h } }
impl Display for Rectangle {
  fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
     write!(fmt, "Rectangle ({} x {})", self.w, self.h)
  }
}

// [...]
let mut r1 = Rectangle::default();
r1.w = 10.; r1.h = 5.;
let ref1 = LightRef::<dyn Shape>::from(&r1);
assert_eq!(mem::size_of::<LightRef<dyn Shape>>(), mem::size_of::<usize>());
assert_eq!(ref1.area(), 50.);

// When not initializing with default, you must initialize the vptr's manually
let r2 = Rectangle{ w: 1., h: 2., ..Default::default() };
let r3 = Rectangle{ w: 1., h: 2., vptr_Shape: VPtr::new(), vptr_ToString: VPtr::new() };

// Also work with tuple struct
#[vptr(Shape)] struct Point(u32, u32);
impl Shape for Point { fn area(&self) -> f32 { 0. } }
let p = Point(1, 2, VPtr::new());
let pointref = LightRef::from(&p);
assert_eq!(pointref.area(), 0.);
```

License: MIT