Skip to content

Commit 775224c

Browse files
authored
Merge pull request #12 from jtakakura/feat/rust-example
Add Rust FFI example
2 parents 86cb5ae + 29614a0 commit 775224c

5 files changed

Lines changed: 99 additions & 0 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ zig-cache/
1212
# Python
1313
__pycache__/
1414

15+
# Rust
16+
target/
17+
Cargo.lock
18+
1519
# Editor
1620
*.swp
1721
*~

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ C example using `libzwasm`: load a module, invoke an export, read the result.
187187

188188
Python ctypes example: same workflow as C, no compiled bindings needed.
189189

190+
### Rust examples (`examples/rust/`)
191+
192+
Rust FFI example: same workflow as C, using `extern "C"` bindings.
193+
190194
## Build
191195

192196
Requires Zig 0.15.2.

examples/rust/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "rust-example"
3+
version = "0.1.0"
4+
edition = "2024"
5+
build = "build.rs"
6+
7+
[dependencies]

examples/rust/build.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use std::{env, path::PathBuf};
2+
3+
fn main() {
4+
let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
5+
let lib_dir = manifest
6+
.join("..")
7+
.join("..")
8+
.join("zig-out")
9+
.join("lib")
10+
.canonicalize()
11+
.expect("zig-out/lib not found — run `zig build lib` first");
12+
13+
println!("cargo:rustc-link-search=native={}", lib_dir.display());
14+
println!("cargo:rustc-link-lib=zwasm");
15+
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir.display());
16+
}

examples/rust/src/main.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//! main.rs — Minimal example using the zwasm C API via FFI
2+
//!
3+
//! Demonstrates: load module, invoke exported function, read result.
4+
//!
5+
//! Build: zig build lib && cargo build
6+
//! Run: cargo run
7+
8+
#[repr(C)]
9+
struct zwasm_module_t {
10+
_private: [u8; 0],
11+
}
12+
13+
#[link(name = "zwasm")]
14+
unsafe extern "C" {
15+
fn zwasm_module_new(wasm_ptr: *const u8, len: usize) -> *mut zwasm_module_t;
16+
fn zwasm_module_invoke(
17+
module: *mut zwasm_module_t,
18+
name: *const std::ffi::c_char,
19+
args: *mut u64,
20+
nargs: u32,
21+
results: *mut u64,
22+
nresults: u32,
23+
) -> bool;
24+
fn zwasm_module_delete(module: *mut zwasm_module_t);
25+
fn zwasm_last_error_message() -> *const std::ffi::c_char;
26+
}
27+
28+
/* Wasm module: export "f" () -> i32 { return 42 } */
29+
const WASM: &[u8] = &[
30+
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f, 0x03,
31+
0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, 0x66, 0x00, 0x00, 0x0a, 0x06, 0x01, 0x04, 0x00, 0x41,
32+
0x2a, 0x0b,
33+
];
34+
35+
fn main() {
36+
unsafe {
37+
let module = zwasm_module_new(WASM.as_ptr(), WASM.len());
38+
39+
if module.is_null() {
40+
let err_msg = zwasm_last_error_message();
41+
let c_str = std::ffi::CStr::from_ptr(err_msg);
42+
eprintln!("Failed to create module: {}", c_str.to_str().unwrap());
43+
return;
44+
}
45+
46+
let name = std::ffi::CString::new("f").unwrap();
47+
let mut results = [0u64; 1];
48+
let ok = zwasm_module_invoke(
49+
module,
50+
name.as_ptr(),
51+
std::ptr::null_mut(),
52+
0,
53+
results.as_mut_ptr(),
54+
results.len() as u32,
55+
);
56+
57+
if !ok {
58+
let err_msg = zwasm_last_error_message();
59+
let c_str = std::ffi::CStr::from_ptr(err_msg);
60+
eprintln!("Invoke error: {}", c_str.to_str().unwrap());
61+
zwasm_module_delete(module);
62+
return;
63+
}
64+
65+
println!("f() = {}", results[0]);
66+
zwasm_module_delete(module);
67+
}
68+
}

0 commit comments

Comments
 (0)