9. February 2023

How to call Rust functions from Python, Ruby and Zig

Rust language is great for creating libraries and drivers which can be consumed by other languages like Python, Ruby or Zig.

Let’s consider following Rust application Cargo.toml:

[package]
name = "rustlib"
version = "0.1.0"
edition = "2021"

[lib]
name = "rust_lib"
crate-type = ["dylib"]

File src/lib.rs:

#[no_mangle]
pub extern "C" fn add(left: usize, right: usize) -> usize {
    left + right
}

The next step is to build the library:

cargo build --release

The output library is stored in ./target/release/librust_lib.so

Python

Invoking the add function from Python is very easy:

import ctypes
lib = ctypes.CDLL("./target/release/librust_lib.so")
lib.add(1,2)

Ruby

In case of Ruby we will need ffi gem.
Installation on OpenSuse:

sudo zypper install ruby3.1-rubygem-ffi

Ruby code:

require 'ffi'

module MyLib
  extend FFI::Library
  ffi_lib './target/release/librust_lib.so'
  attach_function :add, [ :int, :int ], :int
end

puts MyLib.add 1, 2

Zig

Zig will require little bit more stuff. We need to generate C headers from Rust, which then can be loaded to Zig. Install cbindgen for the conversion:

cargo install cbindgen

Generate header file from the library.

cbindgen --lang c --output rustlib.h

Create Zig application in zig2rust:

const std = @import("std");
const rustlib = @cImport(@cInclude("rustlib.h"));

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    const result = rustlib.add(1,2);
    try stdout.print("Result is {d}.\n", .{result});
}

Compile Zig application:

zig build-exe zig2rust.zig -I. -l target/release/librust_lib.so

Run the application:

./zig2rust

21. December 2022

Rust Bare Metal application for ESP32, desktop, Android and iOS

Rust language and tooling are very powerful. It makes it easy to build for different platforms and architectures and still have performance close to C language.

It’s possible to write the same application for ESP32 with Xtensa architecture or ESP32-C3 RISC-V architecture. The portability of Rust code is high. Changing the target and adding a few wrapper functions makes it possible to build the same application for the desktop or web browser with WebAssembly. Once the WebAssembly is ready, it’s possible to turn the application into Progressive Web App (PWA) which can be installed on Android or iOS.

ESP32 Spooky Maze game is example of application which can work on ESP32 and also on mobile device. The shared core code contains the main part of the implementation, and each platform has its tiny wrapper.

The example Rust application uses Embedded Graphics to draw images. When the app is running on real HW, it’s transferred to the display via SPI using ESP-HAL. The desktop version is using SDL2 to interact with Linux, macOS, or Windows. The web version is using WASM, and framebuffer is serialized to HTML5 canvas. PWA application for Android also supports access to Accelerometer, so it can simulate similar behavior like IMU on ESP32-C3-DevKit-RUST or ESP32-S3-BOX where user can tilt the device to move the character in the maze.

Web version of the application with PWA support: Spooky (full-screen mode)

Embedded version of the application: ESP32-S3-BOX, M5CORE-FIRE (M5Stack) and many more.

Thanks to cloud development IDEs, it’s possible to build the application on GitPod.io or GitHub Codespaces and run it even without real HW using Wokwi simulator.

3. May 2022

Podman Debian apt-get update fails with “InRelease is not valid yet”

Podman 4.0.3 users on macOS might face the following strange error when building an image:

apt-get update
...
E: Release file for http://security.debian.org/debian-security/dists/bullseye-security/InRelease is not valid yet (invalid for another 3h 1min 9s). Updates for this repository will not be applied.
E: Release file for http://deb.debian.org/debian/dists/bullseye-updates/InRelease is not valid yet (invalid for another 7h 24min 41s). Updates for this repository will not be applied.

The problem is caused by the not synced clock in Podman VM. This might happen due to the hibernation of the notebook.

The quick fix of the problem is to restart Podman’s VM:

podman machine stop
podman machine start

21. February 2022

Podman: Could not open ‘edk2-aarch64-code.fd’

It’s possible to use brew to install Podman on Apple Silicon (M1). The installation gets slightly more complicated when the user wants to use Homebrew installed in user’s home directory.

Problem #1 – gvproxy

Command:

podman machine start

Error:

Error: unable to start host networking: "could not find \"gvproxy\" in one of ....

Solution: add path to Podman’s helper binaries stored in bin and libexec to ~/.config/containers/containers.conf

[engine]
  helper_binaries_dir=["/Users/georgik.rocks/brew/Cellar/podman/4.0.3/bin","/Users/georgik.rocks/brew/Cellar/podman/4.0.3/libexec"]

Problem #2 – edk2-aarch64-code.fd

Command:

podman machine start

Error:

INFO[0000] new connection from  to /tmp/podman/qemu_podman-machine-default.sock
Waiting for VM ...
qemu-system-aarch64: -drive file=edk2-aarch64-code.fd,if=pflash,format=raw,readonly=on: Could not open 'edk2-aarch64-code.fd': No such file or directory
Error: dial unix /tmp/podman/podman-machine-default_ready.sock: connect: connection refused
ERRO[0003] cannot receive packets from , disconnecting: cannot read size from socket: EOF
ERRO[0003] cannot read size from socket: EOF

Solution: Open file ~/.config/containers/podman/machine/qemu/podman-machine-default.json and change to /Users/USERNAME/brew/Cellar/qemu/6.2.0_1/share/qemu/edk2-aarch64-code.fd

"file=/Users/georgik.rocks/brew/Cellar/qemu/6.2.0_1/share/qemu/edk2-aarch64-code.fd,if=pflash,format=raw,readonly=on"

After these changes Podman should start without problem.

3. November 2021

Pipenv on Windows fails AttributeError: ‘NoneType’ object has no attribute ‘version_sort’

There is an issue with Python Pipenv on Windows.

The command to open shell with isolated pipenv you can use command:

python -m pipenv shell

The command might fail on Windows with a strange error like this:

Traceback (most recent call last):
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.2032.0_x64__qbz5n2kfra8p0\lib\runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.2032.0_x64__qbz5n2kfra8p0\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\__main__.py", line 5, in <module>
    cli()
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\vendor\click\core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\vendor\click\core.py", line 782, in main
    rv = self.invoke(ctx)
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\vendor\click\core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\vendor\click\core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\vendor\click\core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\vendor\click\decorators.py", line 73, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\vendor\click\core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\cli\command.py", line 429, in shell
    do_shell(
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\core.py", line 2356, in do_shell
    ensure_project(
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\core.py", line 576, in ensure_project
    ensure_virtualenv(
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\core.py", line 498, in ensure_virtualenv
    python = ensure_python(three=three, python=python)
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\core.py", line 388, in ensure_python
    path_to_python = find_a_system_python(python)
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\core.py", line 350, in find_a_system_python
    return next(iter(finder.find_all_python_versions()), None)
  File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\pipenv\vendor\pythonfinder\pythonfinder.py", line 328, in find_all_python_versions
    path_list = sorted(versions, key=version_sort, reverse=True)
AttributeError: 'NoneType' object has no attribute 'version_sort'

The problem is caused by pythonfinder.py which is trying to locate a Python.

You can override the pythonfinder.py by explicitly defining the version of the Python.

Determine the version of installed Python:

python --version

Specify the Python version when starting the shell:

python -m pipenv shell --python 3.9.7

The Python environment should start correctly.