# Rustidor #2: Porting Sauerbraten from C++ to Rust

# How to manipulate C++ Data from Rust

In this post, we will learn how to access data that resides in C++ from Rust. Our journey will begin with reading the data, and then we'll explore how to modify it.

Before diving into the specifics, make sure you're following along with the right branch for this tutorial.

```bash
~/$RUSTIDOR_WORKING_DIR/rustidor$ git checkout post#2
```

## The `vec` Problem

Let's take a look at `weapon.cpp`. You'll notice a bunch of references to `struct vec`. What is `struct vec`? It's a class in `geom.h` that holds three `float` values. These floats are pretty versatile – they can be `x`, `y`, `z` coordinates, `r`, `g`, `b` colors, or just an array with three elements.

`struct vec` is also packed with vector and matrix operations. Porting these to Rust might be a bit of a hassle, but it's nothing too tricky. The real snag is that the data in `struct vec` is public. The code is full of direct accesses to stuff like `vec.x`. If we shift this data over to Rust and switch to using getters and setters, we'd have to rewrite all those direct accesses.

Our endgame is to get the whole game running in Rust. But for now, we can keep the `struct` as it is and work with it from Rust.

## Preparing Access to C++ Data from Rust

Alright, so we're keeping the data in the C++ side of things. What we'll do next is whip up some Rust functions that can fiddle with this data and mimic the methods of the `vec` struct.

First things first, create a new file named `vec.rs`. In this file, we're going to create something similar to the C++ `struct vec`. Let's call it `CVec`. This `CVec` struct will be able to hold three values of type `T`. Why make it generic? You'll see the benefits later. Also, we need to let Rust know that it should store these values the same way C does. We do this by adding `#[repr(C)]` right before the struct definition.

Here's what `vec.rs` should look like:

```rust
#[repr(C)]
pub struct CVec<T> {
    pub x: T,
    pub y: T,
    pub z: T,
}
```

## Reading `struct vec`'s Data: Porting of `rst_iszero`

Now let's get our hands on porting the `vec::iszero` method. This method is pretty straightforward – it checks if the `x`, `y`, and `z` values are all zero, and if they are, it returns true.

Here's how it looks in `geom.h`:
```cpp
bool iszero() const { return x==0 && y==0 && z==0; }
```

We're going to switch this out with a call to a new function that does the same thing. This new function will take a vec and check its values.

So, in `geom.h`, we now have:
```cpp
bool iszero() const { return rstd_iszero(this); }
```

And of course, we need to declare this new function. In `rust_port.h`, add:
```cpp
bool  rstd_iszero(const void* v);
```

This way, we're setting up a bridge between our C++ and Rust code, keeping the functionality intact while we shift gears.

But here's the cool part: the implementation of this function will actually be in Rust. Just like we did in the [previous article](https://ninjatic.engineer/rustidor-1-porting-sauerbraten-from-c-to-rust), we'll let the Rust compiler know a couple of things about our function. First, we tell it not to mangle the name, which means keeping the function name as it is. This is important for C++ to recognize it. Second, we use the C calling convention to ensure that our Rust function can be called from the C++ side without any hiccups.


### Imports from `cty` Crate

Alright, let's talk about how our function will interact with the `struct vec`:

- First off, our function will be taking a pointer to `struct vec`. In C++, this is a `void*`, but in Rust, we map it to a `cty::c_void` pointer. This means we're going to need the `cty` crate for its `c_void` type.
- Since our comparison isn't with a float value like `0.0` but with an integer `0`, we'll need to treat our `CVec` struct as if it contained `c_int` values. So, we'll also bring in the `c_int` type from the `cty` crate.

Here's a quick look at what our imports will look like:

```rust
use cty::{c_void, c_int};
```


### Implementation of `rst_iszero`

Now, let's dive into the `rst_iszero` function. What we need to do here is convert the `c_void` pointer into a pointer to `CVec<c_int>`. To peek inside and see what's going on, we'll have to dereference this pointer. Here's the catch: dereferencing a pointer in Rust is considered an unsafe operation, because there's no absolute certainty that the pointer is pointing to the right place. But for now, let's go with it and trust that it's all good.

Here's a look at how we'd implement this:

File `vec.rs`:
```rust
extern crate cty;
use cty::{c_int, c_void};

#[repr(C)]
pub struct CVec<T> {
    pub x : T,
    pub y : T,
    pub z : T,
}

#[no_mangle]
pub extern "C" fn rstd_iszero(vec: *const c_void) -> bool {
    // This is 3 steps:
    // 1. convert vec to a const pointer to CVec holding c_int,
    // 2. dereference it (unsafe)
    // 3. convert it to inmutable reference.
    let vec = unsafe { & *(vec as *const CVec<c_int>) }; 
    
    // Check for zero values
    vec.x == 0 && vec.y == 0 && vec.z == 0   
}
```

Let's touch on an important aspect of our `rst_iszero` function. Remember, our aim here is just to look at the `vec` data, not to change it. That's why in C++, the `rstd_iszero` function takes a `const*`. This tells us, and anyone else reading the code, that we're only reading from this data, not writing to it.

On the Rust side, we follow this principle by converting the pointer to an immutable reference inside `rst_iszero`. This step is more than just a formality; it's about sticking to Rust's safety norms and respecting the data's constancy, just like we promised in the C++ declaration.

### Writing `struct vec`'s Data: Porting of `mul(float f)`

Now, let's shift gears a bit and look at porting a function that actually modifies `struct vec`'s data. We'll focus on the `vec::mul(float f)` function. This function multiplies each component of the vector by a float value. Here's how it's implemented in C++:

In `geom.h`:

```cpp
vec &mul(float f) { x *= f; y *= f; z *= f; return *this; }
```

This function takes a float, multiplies the `x`, `y`, and `z` values of our vector by this float, and then returns the modified vector. Next up, we'll see how to bring this functionality over to Rust, keeping in mind that we're now dealing with modifying data, not just reading it.

As we've seen, the `vec::mul(float f)` function is all about scaling the vector. It multiplies each component (x, y, z) by a scalar float value. Pretty straightforward, right?

Just like we did with `rust_iszero`, we're going to swap out the C++ implementation with a call to a Rust function. This time it's a bit trickier. We need to handle a pointer to the current instance of our vector, perform the data manipulation in Rust, and then return a pointer to the modified instance.

Here's the change in `geom.h`:

```cpp
vec &mul(float f) { vec* v = static_cast<vec*>(rstd_mul(this, f)); return *v; }
```

This approach, where we rely on the `rstd_mul` function in Rust, comes with a bit of a safety concern. We're essentially trusting that `rstd_mul` will return a pointer that matches the `this` pointer. It's a bit of a leap of faith, since we're not verifying this within the function itself.

An alternative would be to modify the data directly within `rstd_mul` and notreturn anything, and leave the `return *this` but, let's stick to the pattern. We can always change it later.

The declaration for rstd_mul is:

File `rust_port.h`:
```cpp
void* rstd_mul(void* v, const float f);
```

And the rust implementation:

File `vec.rs`:
```rust
#[no_mangle]
pub extern "C" fn rstd_mul(vec: *mut c_void, f:c_float) -> *mut c_void {
    let vec = unsafe { &mut *(vec as *mut CVec<c_float>) };
    vec.x *= f; vec.y *= f; vec.z *= f;    
    vec as *mut CVec<c_float> as *mut c_void
}
```

It's crucial to remember that we're altering the data in `vec`, so we need to use mutable pointers. In Rust, this means when we convert the pointer to a reference, it has to be a mutable reference to allow for data modification.

Also, in this context, our `CVec` is going to handle `c_float` types. So, let's update our use declaration in `vec.rs` accordingly:

```rust
extern crate cty;
use cty::{c_int, c_float, c_void};
```

## Bringing the `vec` Module into the Library

To make sure our `rstd_iszero` and `rstd_mul` functions are part of our Rust library, we need to add a new module declaration in our `lib.rs` file. This step is important because it tells Rust to include the `vec` module, which contains our functions, in the library compilation.

Here's what we add in `lib.rs`:

File `lib.rs`:
```rust
use std::os::raw::c_char;
use std::ffi::CStr;

// This is needed for vec functions to be included in the library!
mod vec;
...
```
## Build and check

### Build rustidor library
Now, we must build our lib as a release:
```bash
~/$RUSTIDOR_WORKING_DIR/rustidor$ cd rust_port
~/$RUSTIDOR_WORKING_DIR/rustidor/src/rust_port$ cargo build --release
   Compiling rust_port v0.1.0 (~/$RUSTIDOR_WORKING_DIR/rustidor/src/rust_port)
    Finished release [optimized] target(s) in 0.32s
```

Check that the functions are there. You should see a similar output from the `nm` command:
```bash
~/$RUSTIDOR_WORKING_DIR/rustidor/src/rust_port$ nm target/release/librust_port_lib.a | more

rust_port_lib-bc5126501f3f03b3.rust_port_lib.493c5e149634b1d5-cgu.0.rcgu.o:
0000000000000000 T rstd_getweapon
0000000000000000 T rstd_iszero
0000000000000000 T rstd_mul
                 U __rust_dealloc
                 U strlen
                 U _ZN4core3ffi5c_str4CStr6to_str17h1a09e925bfb46377E
                 U _ZN4core3num62_$LT$impl$u20$core..str..traits..FromStr$u20$for$u20$usize$GT$8from_str17h80670d9e3c2d1c45E

```

### Build Sauerbraten

Now, we've got to tweak the Makefile a bit. This is because the `sauer_server` program also uses the `rstd_iszero` function we've been working on. We need to tell the Makefile where to find our Rust library and how to link it.

Around line 147 in the Makefile, you'll want to add the library directory and the instructions for loading the library:

```make
...
endif
SERVER_LIBS+= -L rust_port/target/release -lrust_port_lib
SERVER_OBJS= \
	shared/crypto-standalone.o \
	shared/stream-standalone.o \
...
```

Then the only remaining steps are to make the game...

```bash
~/$RUSTIDOR_WORKING_DIR/rustidor/src/rust_port$ cd ..
~/$RUSTIDOR_WORKING_DIR/rustidor/src$ make
```

... and launch it:
```bash
~/$RUSTIDOR_WORKING_DIR/rustidor-code/src$ cd ..
~/$RUSTIDOR_WORKING_DIR/rustidor-code$ src/sauer_client
```

The game itself should look as always.

### How to Tell if the Library is Doing Anything?

Want to quickly check if your Rust library is actually kicking in? Here's a simple test. Let's tweak the `rstd_mul` function so it always multiplies by zero. It's a bit of a hack, but it'll clearly show if the Rust code is being used.

Update `rstd_mul` in Rust like this:

```rust
#[no_mangle]
pub extern "C" fn rstd_mul(vec: *mut c_void, f: c_float) -> *mut c_void {
    let vec = unsafe { &mut *(vec as *mut CVec<c_float>) };
    vec.x *= 0.0; vec.y *= 0.0; vec.z *= 0.0; // This is just for testing!
    vec as *mut CVec<c_float> as *mut c_void
}
```

Build the library and the game with these changes, then launch it. You should hear the game's music, but the welcome screen will be missing the menu. It's a clear sign that our Rust library is in action.

That's it for now. See you in the next article! Follow me to stay updated with more Rust and C++ adventures.

