hxn

TUTORIAL / 04

Call Haxe from Rust

Paint a picture with a Haxe function and let callback exceptions reach the caller.

1. Pass a Haxe function

The picture example lets Rust call a Haxe function for each pixel. Here is the Rust method and a Haxe caller:

lib.rs · excerpt
pub fn paint<E>(&mut self, shade: impl Fn(i32, i32) -> Result<i32, E>) -> Result<(), E> {
    for y in 0..self.height {
        for x in 0..self.width {
            let level = shade(x, y)?.clamp(0, 255);
            self.pixels[(y * self.width + x) as usize] = level as u8;
        }
    }
    Ok(())
}
Main.hx · excerpt
var disc = new Picture(25, 9);
disc.paint((x, y) -> {
	var dx = x - 12, dy = (y - 4) * 2;
	return dx * dx + dy * dy <= 36 ? 255 : 0;
});

The callback runs during paint, on the calling Haxe thread. Rust uses ? to stop painting when it fails.

2. Throw from the callback

Main.hx · excerpt
try {
	disc.paint((x, y) -> {
		if (x == 5)
			throw new haxe.Exception('gave up at column $x');
		return 0;
	});
} catch (error:haxe.Exception) {
	Sys.println(error.message);
}

The original Haxe exception reaches the caller of paint after Rust returns. Return Result<T, E> from Rust callback signatures and propagate Err; hxn handles the exception conversion.

3. Borrow objects and return bytes

Main.hx · excerpt
var line = new Picture(25, 9);
line.paint((x, y) -> y == 4 ? 255 : 0);

// One object is passed as a parameter to another object's method.
disc.lighten(line);
lib.rs · excerpt
pub fn pixels(&self) -> Vec<u8> {
    self.pixels.clone()
}

lighten(&mut self, other: &Picture) borrows the second picture for the call. Both Haxe objects remain usable. Vec<u8> becomes haxe.io.Bytes; use pixels.get(index) to read it. Call close() to release a picture promptly; otherwise its finalizer releases it.

Run the example

From the repository root, run the complete example:

Run picture
cd examples/picture
hxn run hl

Full Rust and Haxe source ↗ · Use hxn run cpp for hxcpp.