Hi all, I’ll outline our chosen strategy for the Rust–UNO binding project, explain why we’ve settled on raw FFI against the C UNO API, and show our implementation plan—starting with the very first tasks we’ll tackle.
*Why We Chose Raw FFI* We have chosen to build our Rust–UNO bindings by using the C UNO API directly through raw FFI instead of using cxx.rs or other interop tools. Working with the stable C interface makes our build process easier— avoids the MSVC build errors we saw with recent cxx versions. While this method means writing more manual code to wrap C structs and manage lifetimes, it gives us full control over memory safety and future updates, making maintenance easier down the road. *Evaluation of cxx.rs <http://cxx.rs>* During our review, we found three main problems with cxx.rs. First, mapping UNO’s complicated inheritance structures and heavy use of templates (like Reference<T> and container types) was awkward in Rust’s trait system and cxx’s setup. Second, important C++ features—many types don’t translate cleanly into Rust, leading to fragile wrappers or extra layers. In contrast, Rust’s FFI tools (like bindgen) are well known and reliable. *Implementation Plan* 1. *Bindgen Generation* Our very first task is to run bindgen against the primary UNO C headers (e.g., core API headers). This will automatically generate Rust functions, structs, and enum definitions matching the C signatures. We’ll review the generated code, prune any unnecessary items, and ensure the bindings compile cleanly on all target platforms. This gives us a complete low-level foundation—every UNO function and type we need will already exist in Rust, albeit in an unsafe form. 2. *Safe Wrapper Layer* UNO’s C API relies on manual reference counting via functions like uno_Reference_addRef and uno_Reference_release. We’ll encapsulate that in Rust smart-pointer types—perhaps UnoRef<T>—that call the appropriate add/release functions in their Clone and Drop implementations. This mimics UNO’s Reference<T> semantics and ensures that Rust’s ownership rules align with UNO’s lifetime model, all while preserving zero-cost performance. 3. *Incremental Testing *Testing happens at every stage. Right after bindgen, we’ll write small build-only tests to check that each raw binding compiles and links. After the safe wrapper layer is in place, we’ll call our embindtest FFI service to verify primitive types (integers, floats, strings) and container types (arrays, sequences) behave correctly. Finally, once smart pointers and ergonomic APIs are ready, we’ll write integration tests against a real UNO service—ensuring end-to-end functionality. This step-by-step validation ensures we catch issues early and keep confidence high as the project grows. Best, Mohamed Ali
