QRust(一) 简介

QRust是一个开源组件,是Qt和Rust两种语言的混合编程中间件,是Qt调用Rust函数的支持技术。

QRust来源于工具软件OnTheSSH,OnTheSSH软件由Qt和Rust两种语言共同构建,Rust实现了SSH通讯底层协议,Qt搭建程序界面,Qt调用Rust的技术需求催生出了QRust。

一个使用QRust的例子:

Rust端:

fn invoke(fun_name: &str, mut args: Vec<&[u8]>) -> Result<Option<Vec<u8>>>
{
    match fun_name
    {
        ……
        "foo2" =>
        {
            let a1 = de::from_pack(args.pop().unwrap())?;  //反序列化获得参数1
            let a2 = de::from_pack(args.pop().unwrap())?;  //反序列化获得参数2
            let a3 = de::from_pack(args.pop().unwrap())?;  //反序列化获得参数3

            let ret = api::foo2(a1, a2, a3);    //调用函数foo2,得到返回值
            let pack = ser::to_pack(&ret)?;     //序列化返回值
            Ok(Some(pack))
        }
        ……
    }
}

在上面的代码中,通过匹配字符串”foo2″定位到函数api::foo2(),传入三个反序列化得到的参数,并将函数返回值序列化后返回。

Qt端:

Rust rust("foo2");  //声明要调用的Rust函数foo2

//参数 1
QList<qint32> a1 = {100};
QByteArray ba1 = QRust_Ser::pack_list_i32(&a1);  //序列化
//参数 2
QHash<qint32, QString> a2 = {{1, "abcde"}};
QByteArray ba2 = QRust_Ser::pack_hi_str(&a2);  //序列化
//参数 3
QHash<QString, QString> a3 = {{"a", "12345中文"}};
QByteArray ba3 = QRust_Ser::pack_hs_str(&a3);  //序列化

rust.call(ba1, ba2, ba3);  //调用函数并传参

QHash<QString, QString> ret;  //声明返回值
QRust_De::upack_hs_str(rust.pop(), &ret);  //反序列化获得返回值

在上面的代码中,声明要调用的Rust端函数foo2,序列化并传递三个参数,函数调用后反序列化获得返回值。在示例中,实现了三种复杂数据类型的转换:

Qt端QList<qint32>QHash<qint32, QString>QHash<QString, QString>
Rust端Vec<i32>HashMap<i32, String>HashMap<String, String>

QRust能带来什么?

混合语言编程总是一项具有挑战性的任务,在C/C++和Rust语言之间,Rust调用C函数相对容易一些,在Rust的底层中就存在着大量的用unsafe包裹的C函数的调用语句。反过来C调用Rust就相对复杂一些,特别是传递复杂的参数时,比如集合、自定义的struct、堆上存放的数据,这种场景马上会带来指数级的复杂性和恐怖的代码出错率。

在OnTheSSH的早期版本中,采用了另一种简单的技术实现,Qt以TCP/Socket方式调用Rust服务,通过JSON封装数据,这种方式非常容易实现,但也存在以下问题:

  • 凭空多出了TCP服务,不仅占用资源,在一些环境中还会触发安全提示,对于使用者不是很友好。
  • Socket是网络调用,相比进程内调用性能差了很多。
  • 代码中存在大量的JSON相关的序列化和反序列化语句,显得有些啰里啰唆。

因此需要使用更好的技术来解决以上问题,但Qt调用Rust存在两个技术难点:

  • Qt怎样方便的调用Rust函数,以什么形式调用?
  • 种类众多且复杂的数据类型怎样方便的从Qt传递到Rust,然后从Rust传回Qt?

部分编程语言(如Java)具有运行时的反射机制,可以获得内存中对象的类型和值,能动态调用函数,是进行混合编程的利器。但C++和Rust都没有运行时反射机制(由语言哲学决定),很难在C++和Rust中动态获得或解析变量类型。

在Rust的语言规范中,C语言调用Rust函数有一套接口FFI,但这里我不想介绍FFI因为它太复杂,并不是理想的解决方案。

QRust是为解决以上问题的一种折中的技术实现,它的设计思想体现在这几个方面:

  • 降低FFI接口的复杂性,让使用者通过基础的少量的学习,即可掌握Qt对Rust的调用技术。
  • 减少序列化和反序列化的代码量。
  • 推行实用主义,不追求具有反射能力的完全自动化,也不刻意追求复杂数据类型的传递能力,在Qt和Rust语言的有限条件下,最大限度的提供混合编程的能力。
  • 契合Qt和Rust的规范和习惯,比如在序列化和反序列化技术上,Rust选用标准的serde框架,Qt选用QMetaObject技术来实现struct的遍历和读写。

下载QRust源码

QRust Source Code – onthessh.com

Leave a Reply

Your email address will not be published. Required fields are marked *