Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions examples/rust-struct-ffi/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// A C++ program that creates and uses a Rust Person struct via FFI
//
// Person contains a nested Location struct (city and country). C++ never
// sees either struct — it passes the city and country as plain strings when
// creating the person, and Rust builds the nested Location internally.
//
// Also demonstrates the string return pattern: get_person_info returns a
// Rust-allocated string. C++ reads it, then calls release_get_person_info
// to free it. C++ must never call free() or delete on it directly.
//
// Adapted from: https://github.com/wisonye/rust-ffi-demo

#include <cstdint>
#include <iostream>

// Opaque type — C++ never sees inside this struct
struct Person;

// Declare the Rust FFI functions
extern "C" {
Person *create_person(const char *first_name, const char *last_name,
uint8_t gender, uint8_t age,
const char *city, const char *country);
void print_person(const Person *ptr);
char *get_person_info(const Person *ptr);
void release_get_person_info(char *ptr);
void release_person(Person *ptr);
}

int main(void) {
std::cout << "Creating a Person in Rust from C++..." << std::endl;

// gender: 0 = Female, 1 = Male, 2+ = Unknown
// city and country are plain strings — Rust builds the Location internally
Person *alice = create_person("Alice", "Smith", 0, 30, "Lagos", "Nigeria");

std::cout << "Printing person info (via Rust):" << std::endl;
print_person(alice);

// get_person_info returns a Rust-allocated string.
// C++ reads it, then hands it back to Rust to free.
std::cout << "Getting person info as a string (Rust allocates):" << std::endl;
char *info = get_person_info(alice);
std::cout << info << std::endl;
std::cout << "Freeing the info string (Rust frees):" << std::endl;
release_get_person_info(info);

std::cout << "Freeing the person (Rust Drop runs):" << std::endl;
release_person(alice);

return 0;
}
30 changes: 30 additions & 0 deletions examples/rust-struct-ffi/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# ^ support bash that's not in the standard `/bin` path

# A script that builds, tests, and runs the rust-struct-ffi example

# This file needs to be executable:
# - on Linux or macOS, use `chmod +x run.sh` then `git add run.sh`
# - on Windows, use `git add --chmod=+x run.sh` or `git update-index --chmod=+x run.sh`

# bash "strict mode": fail the script if any command fails, or any variable is unset when used
set -euo pipefail

# Replace unset `$CXX` variable with the empty string
if [ -z "${CXX:-}" ]; then
echo "CXX environment variable is empty or unset, using the default 'c++' compiler"
CXX=c++
fi

# If the script is run outside the example directory, change to the expected location
cd "$(dirname "$0")"

echo "Building and running the rust-struct-ffi example"
# Build the Rust library
pushd rust-library
cargo build
popd
# Build and run the C++ and Rust binary
# This order is important, dependencies must be later than code that depends on them
$CXX main.cpp rust-library/target/debug/librust_library.a -o main
./main
7 changes: 7 additions & 0 deletions examples/rust-struct-ffi/rust-library/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions examples/rust-struct-ffi/rust-library/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "rust-library"
version = "0.1.0"
edition = "2024"

# Build a static C/C++ library
[lib]
crate-type = ["staticlib"]

[dependencies]
Loading