Skip to content

Add example: passing Rust structs to C++ via opaque pointers#46

Draft
thebabalola wants to merge 4 commits intorustfoundation:mainfrom
thebabalola:feature/rust-struct-ffi-example
Draft

Add example: passing Rust structs to C++ via opaque pointers#46
thebabalola wants to merge 4 commits intorustfoundation:mainfrom
thebabalola:feature/rust-struct-ffi-example

Conversation

@thebabalola
Copy link
Copy Markdown
Contributor

@thebabalola thebabalola commented Apr 4, 2026

This builds on the basic math example in #38 by demonstrating a more realistic FFI pattern , passing heap-allocated Rust structs to C++ using opaque pointers.

The example includes:

  • A Rust Person struct with a Gender enum, exposed via extern "C" functions
  • The create/use/release pattern: C++ calls create_person to allocate, print_person to inspect, and release_person to free
  • A Drop implementation that prints when cleanup runs, so you can verify Rust properly frees the memory
  • Null pointer checks on all FFI functions
  • Rust unit tests for creation, release, and null safety
  • A run.sh shell script that builds and links everything

Since C++ never sees inside the Rust struct , and only holds an opaque pointer. This is the standard pattern for passing complex Rust data to C++ without exposing internal layout.

Adapted from: https://github.com/wisonye/rust-ffi-demo

Related: #13

Demonstrates the create/use/release pattern for passing heap-allocated
Rust data to C++ through FFI. C++ holds an opaque pointer to a Rust
Person struct and uses extern "C" functions to create, inspect, and
free it. Includes a Drop implementation that prints when cleanup runs.

Adapted from: https://github.com/wisonye/rust-ffi-demo

Related: rustfoundation#13
@thebabalola
Copy link
Copy Markdown
Contributor Author

thebabalola commented Apr 4, 2026

Hi @teor2345 ... I opened this PR to adapt a real-world FFI example from https://github.com/wisonye/rust-ffi-demo.
It demonstrates passing Rust structs to C++ using opaque pointers and the create/use/release pattern. So, i kept the starting point focused, Person struct with name, gender, and age then added a nested Location struct (city and country) in the follow-up commit to show how nested ownership works across the FFI boundary.

Also, i'm planning to add a string return pattern next (get_person_info / release_get_person_info), but happy to hear if there's a direction you think would be more useful.

Add a Location struct (city, country) as a field on Person.
C++ passes city and country as plain strings to create_person;
Rust builds the Location internally and owns it alongside the Person.

This demonstrates nested struct ownership across the FFI boundary —
C++ never sees Location directly, and both structs are freed together
when release_person is called.
@teor2345
Copy link
Copy Markdown
Collaborator

teor2345 commented Apr 6, 2026

Thanks for this PR, but we're only reviewing one PR per person at the moment.

I've already see your PR #38, so I'm going to mark this one as draft. We can decide what to do with it once your other PR has finished.

@teor2345 teor2345 marked this pull request as draft April 6, 2026 21:57
Add get_person_info and release_get_person_info to demonstrate the
string return pattern across the FFI boundary.

Rust allocates a CString from the person's data and hands the raw
pointer to C++. C++ reads the string, then calls release_get_person_info
to let Rust free it. C++ must never call free() or delete on it directly.

This is a common real-world pattern when Rust needs to return text data
to a foreign caller that doesn't know how Rust manages memory.
Document the key interop differences inline:
- why to_string_lossy is used instead of unwrap (C strings may not be UTF-8)
- why Box::from_raw pairs with Box::into_raw (correct allocator; C++ free() would be UB)
- module-level note on drop order and memory ownership

Add two new tests:
- test_gender_variants: verifies all three gender codes (0/1/2+) produce a valid pointer
- test_info_contains_location: verifies get_person_info output includes both city and country
@thebabalola
Copy link
Copy Markdown
Contributor Author

thebabalola commented Apr 10, 2026

To round up this PR, I added inline comments documenting the key interop differences , why to_string_lossy instead of unwrap (C strings aren't guaranteed UTF-8), why Box::from_raw pairs with Box::into_raw (C++'s free() would be UB), and a module-level note on drop order and memory ownership. Also added two new tests: one for all three gender variants, one verifying get_person_info includes both city and country. with 5 passing tests. The example is complete on my end, ready for your review whenever you have time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants