Using Rust from Perl and Julia

With the recent runtime removal, utilizing Rust libraries from other languages has gotten even better. In this post I am going to take the Rust library that wycats used in this post, and use it from both Perl 5, and Julia.

Getting started

$ cargo new points $ cd points $ mkdir perl julia $ touch Makefile perl/points.pl julia/points.jl

Fix cargo to create a .so instead of a .rlib:

# Cargo.toml [package] name = "points" version = "0.0.1" authors = [ "Paul Woolcock <paul@woolcock.us>" ] [lib] name = "points" crate-type = ["dylib"]

Also, cargo appends a fingerprint to the lib name, so let’s use a Makefile that will fix it so we have an unchanging lib name:

# Makefile all : cargo build ln -fs $(PWD) /target/libpoints- * .so $(PWD) /target/libpoints.so

Ok, let’s get started writing the points library:

First, we bring some traits into scope from the stdlib that we will need. Then we define our data structures.

// src/lib.rs use std :: num ::{ Int , Float }; #[deriving(Copy)] pub struct Point { x : int , y : int } struct Line { p1 : Point , p2 : Point } impl Line { pub fn length ( & self ) -> f64 { let xdiff = self .p1.x - self .p2.x ; let ydiff = self .p1.y - self .p2.y ; (( xdiff .pow ( 2 ) + ydiff .pow ( 2 )) as f64 ) .sqrt () } } // rustc 0.13.0-nightly (62fb41c32 2014-12-23 02:41:48 +0000)

Next, we need to define the functions that we will export for use from the other languages.

# [ no_mangle ] pub extern "C" fn make_point ( x : int , y : int ) -> Box < Point > { box Point { x : x , y : y } } #[no_mangle] pub extern "C" fn get_distance ( p1 : & Point , p2 : & Point ) -> f64 { Line { p1 : * p1 , p2 : * p2 } .length () } // rustc 0.13.0-nightly (62fb41c32 2014-12-23 02:41:48 +0000)

Finally, add a quick test so we can make sure we get the same result everywhere.

# [ cfg ( test )] mod tests { use super ::{ Point , get_distance }; use std :: num :: FloatMath ; #[test] fn test_get_distance () { let p1 = Point { x : 2 , y : 2 }; let p2 = Point { x : 4 , y : 4 }; assert ! (( get_distance ( & p1 , & p2 ) .abs_sub ( 2.828427 ) < 0.01f64 )); } } // rustc 0.13.0-nightly (62fb41c32 2014-12-23 02:41:48 +0000)

Now just compile, and we are done writing our Rust library.

$ cargo test Compiling points v0.0.1 ( file:///home/paul/projects/points ) Running target/points-56b2e7a44489e119 running 1 test test tests::test_get_distance ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured Doc-tests points running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured $ make cargo build Compiling points v0.0.1 ( file:///home/paul/projects/points ) ln -fs /home/paul/projects/points/target/libpoints- * .so /home/paul/projects/points/target/libpoints.so

Using libpoints from perl

We will be using FFI::Raw instead of XS. FFI::Raw is a perl module that wraps libffi , and makes this very easy:

#!/usr/bin/env perl use v5 .10 ; use strict ; use warnings ; use FFI:: Raw ; my $make_point = FFI:: Raw -> new ( "target/libpoints.so" , # library "make_point" , # function name FFI::Raw:: ptr , # return type FFI::Raw:: int , FFI::Raw:: int # argument types ); my $get_distance = FFI:: Raw -> new ( "target/libpoints.so" , "get_distance" , FFI::Raw:: double , FFI::Raw:: ptr , FFI::Raw:: ptr ); my $one_point = $make_point -> call ( 2 , 2 ); my $two_point = $make_point -> call ( 4 , 4 ); my $result = $get_distance -> call ( $one_point , $two_point ); say $result ;

Now, let’s run it and see what we get:

$ perl perl/points.pl 2.82842712474619

Using libpoints from Julia

Julia is even easier to use with our Rust library, as it has a C FFI builtin to the language:

function make_point(a::Int, b::Int) ccall( (:make_point, "./target/libpoints"), # function name & library location Ptr{Void}, # return type (Int64, Int64), # argument types a, b) # arguments end function get_distance(a::Ptr{Void}, b::Ptr{Void}) ccall( (:get_distance, "./target/libpoints"), Float64, (Ptr{Void}, Ptr{Void}), a, b) end t = make_point(2, 2) u = make_point(4, 4) println(get_distance(t, u))

$ julia julia/points.jl 2.8284271247461903