raku & rust: a romance?

Rust is blazingly fast and memory-efficient: with no runtime or garbage collector, it can power performance-critical services, run on embedded devices, and easily integrate with other languages. Rust continues the spirit of C with emphasis on code safety and performance with a compiled approach.

Raku is an open source, gradually typed, Unicode-ready, concurrency friendly programming language made for at least the next hundred years. Raku continues the spirit of Perl with interpreter-like code generation (actually on MoarVM), one-liners, shell-centric, lightweight objects and expressiveness to get code up and running fast.

Both are modern languages and come from the Linux background:

  • strong C heritage via DLLs
  • concurrency oriented (no GIL)
  • Copy-On-Write (COW) semantics
  • built-in language interop (FFI) and (NativeCall)
  • no need for external libraries for interop

In the same way that Perl and C were highly complementary technologies in their heyday, so Rust and Raku are naturally, romantically destined to be linked.

Introducing raku Inline::Rust

Following the nomenclature of raku modules such as Inline::Perl5, Inline::Python, Inline::Go and so on, Inline::Rust is a newly availably “interface” to connect Raku code to Rust dynamic libraries. Unlike some of its brethren, this is rather a zen module in that it provides worked examples and helpful Dockerfile builds to speed up the process, but no code is needed. The standard Rust FFI (Foreign Function Interface) on one side – the term comes from the specification for Common Lisp, which explicitly refers to the language features for inter-language calls as such; it is also used officially by the Haskell, Rust and Python programming languages. The core Raku NativeCall on the other – for calling into dynamic libraries that follow the C calling convention

Inline::Rust is inspired by the excellent Rust FFI Omnibus by Jake Goulding.

The Rust FFI Omnibus is a collection of 6 examples of using code written in Rust from other languages. Rust has drawn a large number of people who are interested in calling native code from higher-level languages. Many nearly duplicate questions have been asked on Stack Overflow, so the Omnibus was created as a central location for easy reference. This reference already covers C, Ruby, Python, Haskell, node.js, C# and Julia – with examples for each of these languages accessing the same Rust library code.

For the purposes of brevity, since the Rust code is common for all, this article will focus on the Raku consumption:

Preamble

We need to start with some basics – follow the README.md at Inline::Rust to try it yourself:

use NativeCall; 

constant $n-path = './ffi-omnibus/target/debug/foo';

Rust FFI Omnibus: Integers

Use the is native Trait to define the external function characteristics:

sub addition(int32, int32) returns int32 
    is native($n-path) { * }

say addition(1, 2);

Rust FFI Omnibus: String Arguments

Raku NativeCall provides traits to control Str encoding:

sub how_many_characters(Str is encoded('utf8')) returns int32 
    is native($n-path) { * }

say how_many_characters("göes to élevên");

Rust FFI Omnibus: String Return Values

Here we get a string back from Rust – the “free” sub means that Raku is responsible for releasing the memory. Raku Pointers can use the .deref method to get their contents:

sub theme_song_generate(uint8) returns Pointer[Str] is encoded('utf8') 
    is native($n-path) { * }
sub theme_song_free(Pointer[Str]) 
    is native($n-path) { * }

my \song = theme_song_generate(5);
say song.deref;
theme_song_free(song);

Rust FFI Omnibus: Slice Arguments

Here the for statement is used to cover over the lack of a direct assignment to CArray … a standard raku Array has a totally different memory layout so the CArray type is provided.

sub sum_of_even(CArray[uint32], size_t) returns uint32 
    is native($n-path) { * }

my @numbers := CArray[uint32].new;
@numbers[$++] = $_ for 1..6; 

say sum_of_even( @numbers, @numbers.elems );

Rust FFI Omnibus: Tuples

Full disclosure – there is an open issue with this pattern:

class Tuple is repr('CStruct') {
    has uint32 $.x;
    has uint32 $.y;
}
sub flip_things_around(Tuple) returns Tuple 
    is native($n-path) { * }

my \initial = Tuple.new( x => 10, y => 20 );
my \result  = flip_things_around(initial);
say result.x, result.y;

Rust FFI Omnibus: Objects

Here we can use a Raku class to wrap a Rust structure, note the free method is now automatically called by the Raku Garbage Collector:

class ZipCodeDatabase is repr('CPointer') {
    sub zip_code_database_new() returns ZipCodeDatabase 
        is native($n-path) { * }
    sub zip_code_database_free(ZipCodeDatabase)         
        is native($n-path) { * }
    sub zip_code_database_populate(ZipCodeDatabase)     
        is native($n-path) { * }
    sub zip_code_database_population_of(ZipCodeDatabase, Str 
        is encoded('utf8'))returns uint32 is native($n-path) { * }

    method new {
        zip_code_database_new
    }

    # Free data when the object is garbage collected.
    submethod DESTROY {        
        zip_code_database_free(self);
    }

    method populate {
        zip_code_database_populate(self)
    }

    method population_of( Str \zip ) {
        zip_code_database_population_of(self, zip);
    }
}

my \database = ZipCodeDatabase.new;
database.populate;

my \pop1 = database.population_of('90210');
my \pop2 = database.population_of('20500');
say pop1 - pop2;

Summary

The raku Nativecall syntax is very straightforward and the C-heritage on both sides shows in the seamless marriage (geddit?) of types.

This results in probably the most concise and natural code on the raku side – take a look at the other examples and make your own judgement!

More to come on some practical applications of this and support for concurrency and gradual typing…

~p6steve

PS. Please do leave comments/feedback on the blog page… here

PPS. Love that raku does not enforce indentation – I can make it fit the narrow width here!

4 Comments

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s