How to Call Rust from Ruby, Java, or C#

You cannot call Rust directly from Ruby, Java, or C# because they run on different runtimes; instead, you must compile Rust into a shared library (`.so`, `.dll`, or `.dylib`) using the `#[no_mangle]` and `extern "C"` attributes, then load that library via each language's Foreign Function Interface (

You cannot call Rust directly from Ruby, Java, or C# because they run on different runtimes; instead, you must compile Rust into a shared library (.so, .dll, or .dylib) using the #[no_mangle] and extern "C" attributes, then load that library via each language's Foreign Function Interface (FFI) bindings.

For Ruby, use the ffi gem to load the library and map C functions. For Java, use the Java Native Interface (JNI) or a tool like jnr-ffi. For C#, use DllImport with System.Runtime.InteropServices. The key is ensuring your Rust code exports C-compatible function signatures and handles memory safely, often by passing raw pointers or using Vec converted to slices.

Here is a minimal Rust library (lib.rs) that works for all three:

// Cargo.toml: [lib] crate-type = ["cdylib"]

#[no_mangle]
pub extern "C" fn add_numbers(a: i32, b: i32) -> i32 {
    a + b
}

#[no_mangle]
pub extern "C" fn get_greeting() -> *const i8 {
    static MSG: &str = "Hello from Rust";
    MSG.as_ptr()
}

Compile it with cargo build --release to generate the shared object in target/release/.

Ruby Example:

require 'ffi'

module RustLib
  extend FFI::Library
  ffi_lib File.join(__dir__, 'target/release/libmy_rust_lib.so') # Adjust path/ext

  attach_function :add_numbers, [:int32, :int32], :int32
end

puts RustLib.add_numbers(10, 20) # => 30

Java Example (using JNR-FFI):

import com.github.jnr.ffi.LibraryLoader;
import com.github.jnr.ffi.annotations.Name;

public interface RustLib {
    RustLib INSTANCE = LibraryLoader.create(RustLib.class).load("my_rust_lib"); // Adjust name

    @Name("add_numbers")
    int addNumbers(int a, int b);
}

public class Main {
    public static void main(String[] args) {
        System.out.println(RustLib.INSTANCE.addNumbers(10, 20)); // => 30
    }
}

C# Example:

using System.Runtime.InteropServices;

class Program {
    [DllImport("my_rust_lib", CallingConvention = CallingConvention.Cdecl)]
    public static extern int add_numbers(int a, int b);

    static void Main() {
        System.Console.WriteLine(add_numbers(10, 20)); // => 30
    }
}

Critical Notes:

  1. Memory Management: Never return Rust objects or strings directly. For strings, return a *const i8 (C string) and let the host language manage the lifetime, or pass a buffer pointer into Rust to fill.
  2. Error Handling: Rust Result types do not map to exceptions in C. Return error codes (integers) or use a global error state variable.
  3. Threading: Ensure your Rust library is thread-safe if the host language calls it from multiple threads. Use #[no_mangle] strictly for C ABI compatibility.