.. _proxy2rust: ********** Proxy2Rust ********** Consumes the proxy IR and generates idiomatic Rust bindings, so that Rust code can call into the bound library across the C ABI. .. warning:: The Rust backend is **experimental**. It covers a subset of the proxy IR and several constructs are not yet supported (see :ref:`Limitations ` chapter). Generated crates and the conventions described here are subject to change. Using the tool -------------- The Proxy2rust tool is invoked as a subcommand of the GNATpolyglot program. It must be provided with at least a proxy file and an output path: .. code:: sh $> gnatpolyglot proxy2rust -o {} * ``-h|--help`` Display Help. * ``-q`` Be quiet. * ``-v`` Verbose output. * ``-o|--output=`` Path to the directory in which the Rust interface should be generated. Directories along the path are created if they do not exist yet. * ``--header-file=`` Prepend the content of the given file to all sources generated by GNATpolyglot. Modules ------- Modules in the JSON Proxy become Rust modules (``mod``), declared in ``src/lib.rs`` and emitted as ``src/.rs``. Nested modules become nested modules. Module, function and field names are lowercased to ``snake_case``; class and enum names use ``PascalCase``. Identifiers that collide with a Rust keyword are escaped with the raw-identifier syntax (``r#priv``, ``r#type`` …). Functions --------- Function placement ~~~~~~~~~~~~~~~~~~~ Free functions of a module become module-level ``pub fn`` items. Functions carrying a ``method`` role instead become inherent methods of the role's class (``&self`` / ``&mut self`` — see `Classes`_), and constructors follow the naming described there. Function names are lowercased to ``snake_case``. Because Rust has no overloading, functions that would otherwise share a name within the same scope (a module's free functions, or one class's methods) are disambiguated with a numeric suffix: the first keeps the base name, the rest become ``..._1``, ``..._2`` … Parameters and return values ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Scalars are passed and returned by value. * Class-typed parameters are passed by reference: ``&Class`` for a constant parameter, ``&mut Class`` for a mutable one. * For a ``method`` function, the controlling first parameter becomes the ``self`` receiver and is dropped from the argument list. * Returned class values follow the ownership rules described under `Ownership`_. Types ----- Scalars ~~~~~~~ Scalars are mapped one-to-one to fixed-size Rust primitives: ==================================== =================== Proxy type Rust type ==================================== =================== ``bool`` ``bool`` character ``u8`` signed int 8 ``i8`` signed int 16 ``i16`` signed int 32 ``i32`` signed int 64 ``i64`` unsigned int 8 ``u8`` unsigned int 16 ``u16`` unsigned int 32 ``u32`` unsigned int 64 ``u64`` float 32 ``f32`` float 64 ``f64`` ``void`` ``()`` ==================================== =================== 128-bit scalars are not supported (see :ref:`Limitations `). Enumerations ~~~~~~~~~~~~ An enumeration in the proxy becomes a ``#[repr(...)]`` Rust enum, sized to the underlying proxy type and deriving the usual value traits. The representation values carried by the proxy are preserved. A proxy enumeration ``Color`` with enumerators ``Red``, ``Green`` and ``Blue`` generates: .. code:: rust #[repr(i8)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Color { Red = 0, Green = 1, Blue = 2, } Classes ~~~~~~~ A proxy class maps to a newtype wrapping the opaque pointer to the internal object: .. code:: rust #[repr(transparent)] pub struct Value(pub(crate) std::ptr::NonNull); The pointer field is ``pub(crate)`` so wrappers can be built and unwrapped across the generated crate's modules while staying hidden externally. Constructors and methods are emitted as inherent functions: * Constructors are named ``new`` (the first one), ``new_default`` (the zero-parameter constructor), and ``new_1``, ``new_2`` … for further overloads, since Rust has no overloading. * ``method``-role functions become ``&self`` methods (or ``&mut self`` when the controlling parameter is mutable); the controlling object becomes the receiver. * For a scalar field, the getter returns a copy, while a companion ``_mut`` accessor hands out a real ``&mut`` reference into the object. .. code:: rust use test::test as pkg; let mut v = pkg::Value::new(10); println!("v = {}", v.get_v()); // copy *v.get_v_mut() = 20; // mutate in place println!("v = {}", v.get_v()); Ownership ~~~~~~~~~ Whether a wrapper frees its underlying object is governed by two things: * **Per type.** A class that has a destructor gets an ``impl Drop`` calling the free function exposed by the proxy, so an owned value frees the object at end of scope. A class that has a copy function gets an ``impl Clone``. * **Per returned value.** When a value is returned, the proxy's ownership annotation decides whether the returned wrapper owns the object. A value returned with ``STATIC`` ownership — and any class returned *by reference* — is wrapped in ``std::mem::ManuallyDrop`` so dropping it never frees the underlying object (a non-owning *view* into the object). Otherwise an owned wrapper is returned, which runs its ``Drop`` (if any) at end of scope. Inheritance ~~~~~~~~~~~ A type that derives from a (non-virtual) parent gets ``Deref``/``DerefMut`` implementations that transparently coerce to the parent type, so a ``&Child`` can be passed wherever a ``&Root`` is expected for class-wide dispatch. .. code:: rust use test::test as pkg; let root = pkg::Root::new(1, 2); let child = pkg::Child::new(3, 4, 5); // Deref coercion: &Child coerces to &Root pkg::p_root(&root); pkg::p_root(&child); Crate layout ------------ Generation produces a complete Cargo crate: .. code:: sh $> gnatpolyglot proxy2rust 2proxy/proxy.json -o 2rust $> find 2rust 2rust/ 2rust/Cargo.toml # package metadata (name = , edition 2021) 2rust/build.rs # links the native proxy library 2rust/src 2rust/src/lib.rs # `pub mod` declaration per module 2rust/src/.rs # one file per module: enums, structs, impls, free functions Each generated module file contains a private ``ffi`` submodule holding the ``extern "C"`` declarations of the proxy symbols; the safe API wraps those calls. Building -------- ``build.rs`` reads two environment variables to locate the statically built, encapsulated proxy library, and emits the right link directives: * ``POLYGLOT_PROXY_LIB_DIR`` — directory containing the static archive. * ``POLYGLOT_PROXY_LIB_NAME`` — name of the archive (without ``lib`` prefix or extension). It also links the platform's required system libraries (``dl`` and ``pthread`` on Linux; ``advapi32`` on Windows, which the GNAT runtime needs). .. code:: sh $> POLYGLOT_PROXY_LIB_DIR= \ POLYGLOT_PROXY_LIB_NAME= \ cargo build --manifest-path 2rust/Cargo.toml Supported platforms -------------------- Proxy2Rust currently supports only 64-bit Linux and Windows platforms (see :ref:`Limitations `).