Este projeto demonstra como integrar código escrito em Rust com aplicações desenvolvidas em Java, Python e Node.js utilizando Foreign Function Interface (FFI). O objetivo é mostrar que é possível introduzir Rust de forma incremental em sistemas existentes, aproveitando seu desempenho e segurança sem substituir toda a base de código.
Cada exemplo ilustra como compilar uma função Rust como uma biblioteca nativa e chamá-la diretamente a partir de outra linguagem. Embora as ferramentas utilizadas sejam diferentes, o princípio subjacente é o mesmo: comunicação entre linguagens por meio de interfaces nativas.
O projeto está organizado em três diretórios principais, cada um contendo um exemplo de integração com uma linguagem diferente:
Cada diretório contém os arquivos-fonte necessários e pode ser construído independentemente.
Este exemplo mostra como chamar uma função Rust a partir de uma aplicação Java usando a JNI (Java Native Interface). A função Rust calcula o fatorial de um número inteiro.
public class Calculator {
static {
System.loadLibrary("rust_jni_demo");
}
public native long factorial(int n);
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.factorial(10));
}
}
A linha System.loadLibrary("rust_jni_demo")
carrega a biblioteca nativa compilada a partir do código Rust. O método factorial
é declarado como native
, indicando que sua implementação está em código nativo.
use jni::objects::JObject;
use jni::sys::{jint, jlong};
use jni::JNIEnv;
#[no_mangle]
pub extern "system" fn Java_Calculator_factorial(
_env: JNIEnv,
_this: JObject,
n: jint
) -> jlong {
if n < 0 {
return 0;
}
if n > 20 {
return 0;
}
let mut acc: i64 = 1;
for i in 1..=n as i64 {
acc = acc.saturating_mul(i);
}
acc as jlong
}
A função Rust é exportada com #[no_mangle]
para preservar o nome e seguir a convenção de chamada C. O nome da função Java_Calculator_factorial
segue a convenção JNI: Java_
+ nome da classe + nome do método. Ela recebe parâmetros compatíveis com tipos C e retorna um valor nativo.
Calculator.h
com o comando javac -h . Calculator.java
.cargo build --release
.java -cp . -Djava.library.path=target/release Calculator
Este exemplo demonstra como criar uma extensão Python em Rust usando a biblioteca PyO3
. A função fibonacci
é escrita em Rust, mas pode ser chamada como se fosse uma função Python nativa.
use pyo3::prelude::*;
#[pyfunction]
fn fibonacci(n: u32) -> PyResult<u64> {
if n > 93 {
return Err(pyo3::exceptions::PyValueError::new_err(
"Fibonacci(n) is too large for u64 when n > 93"
));
}
match n {
0 => Ok(0),
1 => Ok(1),
_ => {
let mut a = 0u64;
let mut b = 1u64;
for _ in 2..=n {
let temp = a.checked_add(b)
.ok_or_else(|| pyo3::exceptions::PyOverflowError::new_err("u64 overflow"))?;
a = b;
b = temp;
}
Ok(b)
}
}
}
#[pymodule]
fn pyrust_demo(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(fibonacci, m)?)?;
Ok(())
}
A macro #[pyfunction]
marca a função fibonacci
para exportação ao Python. O módulo pyrust_demo
é o ponto de entrada carregado pelo interpretador Python. O uso de PyResult
permite retornar erros compatíveis com exceções do Python.
from pyrust_demo import fibonacci
print(fibonacci(30)) # Output: 832040
print(fibonacci(94)) # Raises ValueError
Eu sugeriria criar um venv
antes de mais nada:
python -m .venv
source .venv/bin/activate
Mas você é quem sabe!
maturin
: pip install maturin
ou pip install -r requirements.txt
.maturin develop
python test.py
Não precisa compilar o projeto Rust em separado.
Este exemplo mostra como criar um addon para Node.js usando napi-rs
, que permite escrever extensões nativas em Rust com suporte a N-API, garantindo compatibilidade entre versões do Node.js.
use napi_derive::napi;
#[napi(js_name = "reverseString")]
pub fn reverse_string(s: String) -> String {
s.chars().rev().collect()
}
A macro #[napi]
exporta a função para o ambiente Node.js. O atributo js_name
define como a função será exposta no JavaScript. O tipo String
é automaticamente convertido entre Rust e JavaScript.
const { reverseString } = require('./addon.js');
console.log(reverseString('hello'));
O módulo addon.js
é gerado durante a compilação e atua como um wrapper para o binário nativo.
npm install
npm build run
node index.js
Todos os exemplos demonstram o mesmo conceito central: Rust pode ser integrado a sistemas existentes por meio de interfaces nativas. Apesar das diferenças nas ferramentas — JNI, PyO3, napi-rs — o padrão é consistente:
Essa abordagem permite migrações graduais, onde componentes críticos de desempenho são reescritos em Rust, enquanto o restante do sistema permanece funcional.
Este modelo é amplamente utilizado em produção para melhorar eficiência, segurança e confiabilidade sem interromper serviços existentes. ```