Rust und Vala: Ein interessantes Team
Ich bin vor einigen Tagen zufällig über Mozillas (relativ) neue Programmiersprache Rust gestolpert und war direkt angetan. Natürlich war die erste Frage, die sich mir stellte, ob man Rust wohl auch von Vala aus nutzen kann, da es für geschwindigkeitskritische Anwendung sehr gut geeignet scheint.
Wie in der Anleitung gut beschrieben ist, lassen sich Rust-Bibliotheken von anderen Sprachen aus nutzen, darunter natürlich auch C. Da Vala zu C kompiliert wird, lag der Verdacht also nah, dass es auch möglich sein muss, Rust von Vala aus zu nutzen. Und der Weg dorthin wird durch diese Annahme eigentlich auch schon vorgegeben.
Schreiben wir uns also eine kleine Rust-Bibliothek, die uns als Test dienen soll. Ich habe von Anfang an Cargo genutzt, da es sich einfach bedienen lässt und lästige Routineaufgaben übernimmt. Erstellen wir also ein leeres Cargo-Projekt:
Nachdem wir in das erzeugte Verzeichnis vala_test
gewechselt sind, müssen wir zunächst
an die Datei Cargo.toml
anhängen, um Cargo mitzuteilen, dass wir eine dynamisch gelinkte Bibliothek (crate-type = ["dylib"]
) mit dem Namen libvalatest.so
(name = "valatest"
) erstellen wollen.
Anschließend öffnen wir die bereits vorhandene lib.rs
im Ordner src
und fügen folgenden Quelltext ein:
Wie man leicht erkennt, handelt es sich bloß um eine einfache Funktion, die die übergebenen Paramter auf der Konsole ausgibt. Aber genau das ist es, was wir brauchen, um zu sehen, ob unser Programm richtig funktioniert. Zum weiteren Testen habe ich außerdem ein struct
eingefügt.
Wichtig ist hier vor allem das pub extern
, das angibt, dass diese Funktion von außerhalb genutzt werden soll, sowie #[no_mangle]
, das verhindert, dass die automatische, interne Namensänderung von Rust stattfindet.
Nun muss die Bibliothek noch kompiliert werden. Dies erledigt der Befehl cargo build
, das Shared Object befindet sich danach im Ordner target/debug
.
In der Rust-Dokumentation wird richtig erklärt, dass man natürlich eine C-Header-Datei benötigt, um auf die Funktionen der Rust-Bibliothek zugreifen zu können. Schreiben wir uns also so eine Header-Datei libvalatest.h
:
Wie man sieht, ist auch dies relativ trivial. Es müssen lediglich die Rust-Typen auf ihre entsprechenden C-Typen umgemapped werden. Nun folgt der Standard-Vala-Weg, also das Schreiben des VAPIs libvalatest.vapi
:
Wie dem aufmerksamen Betrachter nun vielleicht schon aufgefallen ist, hat die Funktion test
in der C-Header-Datei einen zusätzlichen Parameter vom Typ int
. Dieser wird von Vala automatisch eingefügt, wenn in einem VAPI vor einem Array kein [CCode (array_length = false)]
steht. Und nach meiner Erfahrung in der Zusammenstellung dieses Beispiels sollte man das auch nicht tun. Fehlt die Länge des Arrays in der Funktion, läuft die Schleife for element in slice
in Rust bis zum Erreichen eines Segmentation faults (ich vermute durch den gesamten dem Programm zur Verfügung stehenden Speicher). Warum das so ist, und wie Rust intern die Länge des Arrays aus dem Funktionsaufruf übernimmt kann ich nicht beantworten. Hier wäre jemand mit mehr Erfahrung in Rust wohl der bessere Ansprechpartner.
Nun folgt abschließend ein kleines Vala-Programm main.vala
, dass die Rust-Funktion aufruft und den Datentyp nutzt:
Der Befehl zum Kompilieren ist diesmal ein wenig länger, da alle Komponenten nicht in Standardverzeichnissen liegen:
Zudem muss das Verzeichnis target/debug
noch zu den Verzeichnissen hinzugefügt werden, in denen nach Shared Objects gesucht wird. Dies erledigt ein:
Gestartet wird das Programm nun mit ./main
und tatsächlich, auf der Konsole erscheint die erwartete Ausgabe:
Es ist also sehr gut möglich, von Vala aus Rust zu nutzen, und ich denke, ich werde das in nächster Zeit auch mal tun. Im Laufe dessen werde ich hier natürlich weitere Anleitungen, zum Beispiel zu weiteren Typen wie String
/str
, veröffentlichen.
Bis dahin erst mal viel Spaß mit den neuen Möglichkeiten!