[DSP] Moving away from Rust

This is a part of my Daj się poznać / Get Noticed! 2017 series. It is for a contest where I have to write a blog post twice a week abd create a open source project. Read more about it here.

I spend last few weeks trying to understand how to connect QML and Rust and slowly realizing some things I want are probably just impossible to do. This is mostly due to Rust’s safety features, more precisely variable lifetime which did not want to play along nicely with external GUI lib such as QML.

I was prepare to abandon the project in this form and maybe rewrite it in some different language in future, but nice fellows on devspl Slack convince me to try a different approach. Following their advice (I think it was mostly krzaq), I decided to separate graphical frontend completely (probably to Python) and leave hard-core application login as it is in Rust. So yeah, title is a little clickbait-ish, but what can I do…

How to

As a first step, I decided to move the logic to a separate crate, called bletchley_lib. It couldn’t have been easier, actually. All I had to do was to generate a crate with cargo new bletchley_lib, then move all the dependencies to its Cargo.toml and fix some visibility issues. I my main app (which right now serves as a CLI application) I had to put this in Cargo.toml:

[dependencies]
clap = "~2.19.0"
bletchley_lib = { path = 'bletchley_lib' }

Now, to have everything in order, I created another one crate, called bletchley_ffi. Its goal is to separate ugly glue code, which will be used by FFI-enabled front-end app, from the core logic. For a start, I only glued one function - called it encrypt. It takes two strings: filename of key file and filename of the file to encrypt.

The resulting code in bletchley_ffi/src/lib.rs looks like this:

extern crate libc;
extern crate bletchley_lib;

use std::ffi::{CStr};
use std::os::raw::c_char;
use bletchley_lib::{crypto,ble};

#[no_mangle]
pub extern "C" fn encrypt(key: *const c_char, input: *const c_char) -> i32 {
    unsafe {
        let c_key = CStr::from_ptr(key).to_str().unwrap();
        let c_input = CStr::from_ptr(input).to_str().unwrap();

        let crypt_result = crypto::encryptor::with_file_key(c_key, c_input);
        ble::format::create_file(crypt_result, None);
    }
    return 0;
}

A couple of things as a comment:

  • First we have a bunch of imports required for making FFI-compatible output.
  • A #[no_mangle] directive is necessary so Rust compiler does not change function names into its own internal names, like it usually does.
  • Everything is pretty unsafe, so it has to be wrapped in unsafe block.
  • I always return zero for simplicity now, since it’s either success or miserable failure with a complete crash.

To check if it works, I created a following Python script:

from ctypes import cdll
import platform

extensions_by_system = {
    "Linux": "so",
    "Darwin": "dylib"
}
extension = extensions_by_system[platform.system()]
lib = cdll.LoadLibrary("target/debug/libbletchley_ffi." + extension)
lib.encrypt("key_file.pub", "file.pdf")

It’s nothing too complicated, but… it works! The only thing worth mentioning is that depending on operating system, cargo build of a dylib-type crate generates .so (Linux), .dylib (Mac) or .dll (Windows). Because of that, we need to have additional code to load a lib with a proper file name.

So, it looks like the project is kind of back on the track. In next few days I’ll try to finally put some windowed application which would use underlying Rust logic.

Related posts