You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Alex Williams c1b4e5dae4
Use more idiomatic 'if let'
11 months ago
src Use more idiomatic 'if let' 11 months ago
.gitignore Add initial code and README 12 months ago
Cargo.toml Add initial code and README 12 months ago
LICENSE First commit 12 months ago
Makefile Add initial code and README 12 months ago Add initial code and README 12 months ago
extract.l Add initial code and README 12 months ago

PicoLisp FFI with Rust

This repo provides a simple example of how to use PicoLisp with Rust using PicoLisp's FFI (native) functionality.


  • Rustc and Cargo v1.47.0+
  • PicoLisp 64-bit v17.12+ or pil21

Getting started

Once you've setup PicoLisp and Rust, simply type make to build and test the shared library.


Before I explain what's going on, here's what the output should look like:

Received struct: PilStruct {
    byte1: 32,
    byte2: 33,
    character1: 67,
    character2: 68,
    int: -1,
    long: 1,
    string: 0x4847464544434241,
    array: [
Result code: 0
Extracted struct:
(de Extracted (42 43)
   ("A" "B")
   (80 105 99 111 76 105 115 112) )


The code can be found in extract.l and src/ The Rust code is designed as a shared library and can be called by PicoLisp's (native) function to pass data to/from between both languages.

PicoLisp code explanation

First, the code allocates 32 bytes of memory, which will be used to store data in a struct.

It then creates a struct named P with the following specification:

  • 2 arbitrary bytes
  • 2-bytes containing valid UTF-8 characters
  • 1x 32-bit (4 bytes) signed integer
  • 1x 64-bit (8 bytes) signed long
  • 1x 8-byte null-terminated string
  • 1x 8-byte arbitrary bytes array

Then the following native call is made and its result is stored in the Res variable:

(native "target/debug/" "extract" 'I P)

This calls the extract function from the Rust library, with the P struct as its only parameter. It expects a 32-bit signed integer I as the return value (it will be either 0 or -1).

Next, the code will extract the P structure using the specification described above:

(struct P '((B . 2) (C . 2) I N S (B . 8)))

Finally, the code will free the previously allocated memory and print the result of the P structure.

Some tests run at the end to ensure the data received from Rust is what we expected.


  • The values sent to the Rust library will be printed by Rust as Received struct:.
  • The values received from the Rust library will be printed by PicoLisp as Extracted struct:.

Rust code explanation

The Rust code defines the struct for the received data; it is named PilStruct and contains the exact same specification as the P struct in the PicoLisp code explanation.

The extract() function creates a new struct in the variable newstruct which contains some new values, different from what was received by PicoLisp.

Since FFI is considered unsafe in Rust, the code which actually does the FFI (dereferencing the pointer and writing to it) is contained in an unsafe block:

unsafe { ... playing with fire ... }

Luckily, the Rust function checks if the struct is a null pointer before trying to work with it, and returns -1 if it is. However it doesn't check if it's correctly aligned (it is), so look-out for that!

The code then dereferences the pointer and prints what it received (the entire struct) from PicoLisp as Received struct: (mentioned earlier).

Finally, it writes the newstruct struct to the pointer and returns 0. PicoLisp can then read the return code and the new struct data.


There isn't much to this code, but I thought it would be fun to create a working FFI library that's not written in C and which works perfectly with PicoLisp.




MIT License

Copyright (c) 2020 Alexander Williams, On-Prem