Wie man mit Rust performante und resiliente Apps baut

08.09.2021 Raffael Schneider
Tech Rust memory-safety devops cli distributed-systems concurrency howto tutorial

Mit Kotlin und Go und der impliziten Nutzung von TypeScript haben wir bereits eine Vielzahl von modernen Programmiersprachen im Einsatz. Mit Kotlin haben wir eine Sprache, die den JVM-Stack mit syntaktischen Zucker perfekt ergänzt und die Codebase sinnvoll schlanker macht. Dazu haben wir bereits letztes Jahr eine kurze Einführung geschrieben. Auch zu Go gibt es eine Einführung die wir aufgesetzt haben als wir mit Go-Entwicklung intern angefangen haben. Es gibt mittlerweile zahlreiche Applikation, die wir komplett in Go umgesetzt haben und sind froh, dass wir uns Go als Skill einverleiben konnten, da die Vorteile bei der Entwicklung offensichtlich sind. Mit dieser Euphorie möchten wir auch Rust als Programmiersprache besser verstehen und abwägen ob und zu was sich diese relativ neue Sprache am besten eignet würde.

Ausgangspunkt unserer Anstrengung Rust auszuwerten ist die konsekutive Top-Platzierung im berühmten alljährlich veröffentlichen StackOver Developer Survey. Dabei belegt Rust seit 2016 den ersten Platz bezüglich Most Loved Programming Language. Dies können wir nicht einfach dem Zufall überlassen und haben uns dadurch entschieden Rust unter die Lupe zu nehmen und hoffen dass wir dadurch auch Ihnen einen Mehrwert in Bezug auf Qualität und Nachhaltigkeit geben können.

Ursprünge von Rust

Rust wurde ursprünglich durch den Mozilla-Mitarbeiter Graydon Hoare als persönliches Projekt entwickelt. Mitgewirkt hat dabei auch der bekannte Co-Founder von Mozilla, Erfinder von JavaScript und heutiger CEO von Brave Software, Brendan Eich. Rust fand seither intensiven Einsatz in Mozilla-Produkte. Einflüsse sind neben der Allzwecksprache C++ ganz klar Haskell und Erlang, zwei funktionale Programmiersprachen, die in der Kombination mit einer Maschinennahe-Sprache wie C++ ein durchaus interessante Kombination eingehen.

Mittlerweile nutzen viele Betriebe Rust als Systems Programming-Programmiersprache, wo Speicherallokation und Security eine grosse Rolle spielen. Es gibt ein komplettes Unix-like Betriebssystem Redox, welches in Rust geschrieben ist und auch Microsoft nutzt Rust um neue Windows-Komponenten zu schreiben. Zudem gibt es bekannte Big-Tech Player die Rust in ihrem Stack dazuzählen, darunter nennenswert ist Dropbox, Figma, Discord, 1Password und nicht zuletzt Facebook.

Eine durch die COVID19-Pandemie hervorgerufene Umstrukturierung zwang Mozilla die Entwicklung von Rust und dessen Ökosystem in eine separate Stiftung zu geben. Somit gibt es seit Februar 2021 eine dedizierte Rust Foundation, welche durch AWS, Huawei, Google, Microsoft und weiterhin Mozilla finanziell unterstützt wird um dessen Weiterentwicklung und Fortbestand garantieren zu können.

Einsatzgebiete für Rust sind als eine General Purpose-Programmiersprache laut der hauseigenen Doku breit gefächert und beinhalten unter anderem Command-Line Tools, Web-Services, DevOps-Tooling, eingebettete Systeme, Audio- und Video-Analysis, sowie Transcoding, Cryptowährungen, Bioinformatik, Suchmaschinen, Internet-of-Things Applikationen, Machine Learning, und nicht zuletzt grosse Teile des Firefox Web-Browsers. Beeindruckend wie sich eine relativ neue Sprache wie Rust bereits in verschiedensten Branchen etablieren konnte. Dies möchten wir nun ein wenig mehr auf den Grund gehen und schauen was Rust denn genau auszeichnet.

Merkmale von Rust

Rust ist wie eingangs erwähnt eine Allzwecksprache und eignet sich für alle Art von Systemprogrammierung. Hauptmerkmale legt dabei Rust auf die Ausgewogenheit von Performance und Systemstabilität. Dabei spielt der Compiler eine ganz zentrale Rolle, da er, nicht ganz ähnlich wie bei Go, opiniated, also weiss war er akzeptiert und was nicht, ist. Üblicherweise prüfen Compiler beim Bauen einer Codebase ob sich der Code auf Grund von Syntax und Typen (bei typisierten Sprachen) valide sind und überlässt eine ganze Klasse von potenziellen Fehlern der Laufzeit.

Bei konventionelle Sprachen wie Java, C++ oder auch Go, läuft zur Laufzeit hingegen neben der eigentlichen Applikationslogik auch einen Garbage Collector, welcher automatisch Memory Management betreibt und in einfachen Worten gehalten obsolet gewordene Speichereinheiten zur Wiederverwendung frei gibt. Rust verfügt über keinen Garbage Collection und prüft zur Compilezeit mit einer sogenannten Escape-Analysis ob der Code zu keiner Speicher-induzierten Fehlern führen könnte. Das heisst, das Rust durch das Weglassen des Garbage Collector schlankere und perfomantere Artefakte bauen kann. Rust bietet als Kompensation für das Garbage Collecting ein Ownership-Verständnis, welcher sich ein angehender Rust-Programmierer zuerst aneignen muss. Wir erklären dieses Ownership-Konzept zu einem späteren Zeitpunkt in diesem Beitrag.

Bevor wir weiter auf die Eigenschaften von Rust eingehen, hören wir uns zuerst einmal an wie die offizielle Rust-Dokumentation ihre eigene Sprache –am besten, wie ich denke– zusammenfasst:

Rust is for people who crave speed and stability in a language. By speed, we mean the speed of the programs that you can create with Rust and the speed at which Rust lets you write them. The Rust compiler’s checks ensure stability through feature additions and refactoring. This is in contrast to the brittle legacy code in languages without these checks, which developers are often afraid to modify. By striving for zero-cost abstractions, higher-level features that compile to lower-level code as fast as code written manually, Rust endeavors to make safe code be fast code as well.

The Rust language hopes to support many other users as well; those mentioned here are merely some of the biggest stakeholders. Overall, Rust’s greatest ambition is to eliminate the trade-offs that programmers have accepted for decades by providing safety and productivity, speed and ergonomics.

The Rust Programming Language Documentation, Introduction [Quelle]

Man merkt, dass bei der Entwicklung von Rust das Problem im Vordergrund stand, wie man gewünschten Programmier-Komfort in Form von Abstraktions- und High-Level-Eigenschaften mit einer Fokussierung auf Maschinen-nahe Performanz erzielen kann; die sogenannten zero-cost abstractions.

Um die Features und Benefits von Rust nochmals kurz zusammenzufassen, kann man folgende Liste berücksichtigen:

  • Konsistente Speichersicherheit garantiert durch den opiniated Rust-Compiler

  • Explizite Nebenläufigkeit und Parallelität durch Data Ownership Model

  • Abstraktion und Entwicklungskomfort zum Nulltarif (heisst keine Performance-Einbussen)

Nun, jetzt haben grob beschrieben was Rust auszeichnet. Für Techies, die sich noch nie mit Maschinennahe Konzepten auseinandersetzen mussten, sind vielleicht das eine oder andere Konzept komplett neu und wissen wahrscheinlich im ersten Schritt noch nicht so genau was damit anzufangen ist. Aus diesem Grund sollten wir uns zu allererst das wichtigste bei Rust anschauen: das Data Ownership Model. Hoffentlich wird dadurch alles ein wenig verständlicher.

Ownership, Borrow Checking und Escape Analysis

Wie bereits angekündigt ist das grösste Merkmal bei Rust die Absenz von Garbage Collecting und das damit verbundene Konzept von Ownership, Englisch für Besitz oder Eigentum, aber in diesem Kontext eher am besten übersetzt mit Inanspruchnahme. Das Konzept von Ownership definiert den Lebenszyklus von Daten zur Laufzeit eines Rust-Programms.

Die Rust-Dokumentation definiert für das Konzept von Ownership 3 Regeln. Diese Regeln werden durch den Borrow Checker, ein Feature des Rust-Compilers, bei der Buildtime geprüft um sicherzustellen, dass der Lebenszyklus einer Dateneinheit valide ist. Der Borrow Checker nutzt hierfür ein im Compilerbau bekanntes Pattern mit dem Namen Escape Analysis. Escape Analysis verfolgt die Laufzeit einer Variable bis sie aus dem Geltungsbereich (Scope) fällt und entscheidet anhand davon was mit dem freigewordenen Speicherallokation gemacht werden muss. JVM-Sprachen wie Java oder allgemein Garbage-Collected Sprachen machen dies zur Laufzeit über dynamische Allokation von Variablen. Zurück zu den 3 Regeln. Die Regeln sind wie folgt (freilich aus dem Englischen übersetzte):

1. Each value in Rust has a variable that’s called its owner.
Jeder Wert hat in Rust eine Variable, und diese Variable ist dessen Besitzer.

2. There can only be one owner at a time.
Es kann stets immer nur einen Besitzer eines Wertes zu einem gegebenen Zeitpunkt geben.

3. When the owner goes out of scope, the value will be dropped.
Wenn der Besitzer den Geltungsbereich (Scope) des Wertes verlässt, wird der Wert getilgt.

Es ist sinnvoll kurz den Unterschied von Heap und Stack aus der Maschinennahen Speicherverwaltung in Erinnerung zu rufen. Mit Heap bezeichnet man dynamischer Speicher, welcher zur Laufzeit zusammenhängende Speicherabschnitte wieder beliebig freigeben kann, sobald diese zur Wiederverwertung gekennzeichnet werden. Mit Stack hingegen ist der statische Stapelspeicher der nach dem Last-In-First-Out-Prinzip, kurz LIFO, Datenwerte aufnimmt und wieder abgibt. Zur Laufzeit werden typischerweise Datenwerte in der Heap aufgenommen wenn dessen Lebenszyklus, sprich die potenzielle Mutabilität (verändert sich der Wert oder nicht) nicht bereits im voraus bekannt ist. Genauer gesagt prüft ein Compiler wie bei C++ zur Buildtime den Scope einer Variable und definiert dabei, ob dieser Wert eine kurze, im voraus bekannte Lebensdauer hat oder nicht, und anhand davon ob der Wert in die Heap oder in den Stack aufgenommen wird.

Hier haben wir ein Beispiel (geborgt von dev-notes.eu), der das in Rust-Code veranschaulicht.

fn main() -> Result<(), &'static str> {
    let mut a = [1,2,3,4];
    println!("{:?}", a); // Line 1 output
    
    {
        let b = &mut a[0..2];
        // You can't access a at this point because it has been mutably borrowed
        // from. The following line won't compile, with the error message:
        // `cannot borrow `a` as immutable because it is also borrowed as mutable`:
        // println!("a: {:?}", a);

        println!("b: {:?}", b); // Line 2 output
        b[0] = 42;
        println!("b: {:?}", b); // lIne 3 output
    }

    // The borrow is scope-dependent - a is accessible again because the 
    // variable that borrowed it is out of scope:
    println!("a: {:?}", a); // Line 4 output
    Ok(())
}

Output:

[1, 2, 3, 4]
b: [1, 2]
b: [42, 2]
a: [42, 2, 3, 4]

Somit kann man sagen, dass es stets zwei Arten von Referenzierung von Werten geben kann:

  • Geteilte (shared) Referenzen mit &

  • Mutable (variable) Referenzen mit &mut

Des weiteren kann eine Referenz zu einem Wert nie länger existieren als der Wert, der referenziert wird. Die Idee von Ownership geht bei Rust noch ein bisschen weiter und macht die ersten Schritte in der Sprache ein wenig aufwändiger, da man das Management von Datenlebenszyklen wahrscheinlich in anderen, bekannteren Sprachen wie Java oder Python abstrahiert ist und nicht Teil der Aufgaben bei der Programmierung sind. Man muss hier einfach verstehen, dass Rust damit die Möglichkeit schafft korrekten Code zu schreiben, welcher mit einem Hauptmerkmal auf Security und Safety legt, was dessen Artefakte –vorausgesetzt man weiss was man tut– unglaublich robust und stabil macht. Wenn Sie mehr über Ownership und der Umgang mit Lebenszyklen von Daten bei Rust kennenlernen möchten, kann ich die offizielle Dokumentation empfehlen:

https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html

Installation mit RustUp

Um Rust zu installieren gibt es eine Toolchain, genannt rustup. Bei einer Unix-artigen Umgebung wie GNU/Linux oder macOS kann man direkt über folgenden Command rustup runterziehen und den Installationsprozess damit veranlassen:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

ℹ️ Bei Windows-Maschinen genügt es das rustup-init.exe downloaden und ausführen. Mehr Information dazu unter https://forge.rust-lang.org/infra/other-installation-methods.html

Das ausgeführte Shell-Skript installiert neben rustup, der eigentlichen Toolchain-Verwaltung, auch folgende Elemente:

  • rustc: Der eigentliche Rust-Compiler welcher aus Rust-Sourcecode die Binaries generiert.

  • rustfmt: Ein Format-Binary welche, ähnlich wie go fmt den Sourcecode automatisch entsprechende formattiert.

  • cargo: Cargo ist das Build-System und Package-Manager von Rust. Damit werden wir unsere Rust-Projekte bauen und eventuelle Dependencies auflösen.

  • std: Zuletzt werden noch die Standard-Libs der Sprache mitinstalliert.

Hello b-nova!

Eine ‘Hello b-nova!’-Funktion ist in Rust relativ schnell geschrieben. Man kann für die ersten Gehversuche ohne gleich Rust auf der lokalen Maschine installieren zu müssen in der offiziellen Rust Playground vornehmen.

fn main() {
    println!("Hello b-nova!");
}

Dazu einfach die Playground öffnen, Code-Snippet reinkopieren und Run klicken! Man sieht schön den Output des Build-Prozess, sowie den eigentlichen Output bei dessen Ausführung.

--- Standard Error ---
   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.27s
     Running `target/debug/playground`
     
--- Standard Output ---
Hello b-nova!

Bei Interesse kann man gleich das offizielle Tutorial in der Playground vornhemen, das sogenannte Tour of Rust. Dieses ist gleich aufgebaut und geht über die ganze Sprach-Spezifikation durch und lernt dabei wie man mit Rust if/else- und for-Schleifen schreibt, was die einzelnen Datentypen sind, usw. Schaut vielleicht gleich am besten in die Table of Content des Tutorials rein, welches nebenbei bemerkt komplett auf Deutsch übersetzt ist.

Bauen mit Cargo

Sobald wir ein richtiges Rust-Projekt aufsetzen, ist Cargo ein wichtiges Element im kompletten Lebenszyklus der zu bauenden Applikation. Stellt hierbei sicher, dass Cargo korrekt installiert ist und die Version von Rust auf der neusten Version ist.

❯ cargo --version && rustc --version
cargo 1.54.0 (5ae8d74b3 2021-06-22)
rustc 1.54.0 (a178d0322 2021-07-26)

Mit $ cargo new hello-bnova wird Cargo mitgeteilt dass wir ein Projekt mit dem Namen hello-bnova haben möchte. Cargo übernimmt und stellt uns ein komplettes Verzeichnis zur Verfügung.

❯ cargo new hello-bnova
     Created binary (application) `hello-bnova` package

Cargo erstellt nicht nur das Verzeichnis, sondern gibt bereits eine kanonische Projektstruktur vor, welche neben .git und einer .gitignore auch den src/-Folder, welche eine main.rs beinhaltet, bestimmt, sowie Cargo.toml und Cargo.lock Dateien. Sehr nützlich wenn Sie mich fragen.

❯ tree -a
.
├── .git
│   └── ...
├── .gitignore
├── Cargo.lock
├── Cargo.toml
└── src
    └── main.rs

Bevor wir die main.rs aufmachen, werden wir kurz über die Cargo.toml drüber schauen und erklären was es damit auf sich hat.

Cargo.toml

[package]
name = "hello-bnova"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

Wie schon vermutet handelt es sich bei dieser toml-Datei um eine Meta-Datei der Rust-Applikation. Anhand dieser Datei löst Cargo anfällige Dependencies auf und weiss wie mit die Applikation beschaffen ist. Hier werden wir später eine Dependency pflegen und schauen wie Rust das macht.

Die src/main.rs beinhaltet das gleiche Snippet welches wir vorhin auf dem Playground ausgeführt hatten.

main.rs

fn main() {
    println!("Hello, world!");
}

Mit der Cargo-CLI können wir jetzt das soeben erstellte Projekt bauen. Normalerweise geht das mit cargo build, aber wir können das Kompilieren sowie die Ausführung dessen Artefakt in einer einzigen Zeile ausführen lassen.

❯ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `/Users/rschneider/Development/rust/hello-bnova/target/debug/hello-bnova`
Hello, world!

Jetzt möchten wir ein wenig damit rumspielen und werden das beliebte CLI-Framework Clap einsetzen. Fügen wir zuallererst die Dependency https://crates.io/crates/clap hinzu. Diese liegt im Moment des TechUps in der Version 2.33.3 vor.

Cargo.toml

...
[dependencies]
clap = "2.33.3"

In der main.rs bauen wir gleich das Grundgerüst von Clap mit ein.

main.rs

// (Full example with detailed comments in examples/01b_quick_example.rs)
//
// This example demonstrates clap's full 'builder pattern' style of creating arguments which is
// more verbose, but allows easier editing, and at times more advanced options, or the possibility
// to generate arguments dynamically.
extern crate clap;
use clap::{Arg, App, SubCommand};

fn main() {
    let matches = App::new("Hello b-nova!")
                          .version("1.0")
                          .author("Raffael Schneider <raffael.schneider@b-nova.com>")
                          .about("Says Hello b-nova!")
                          .arg(Arg::with_name("config")
                               .short("c")
                               .long("config")
                               .value_name("FILE")
                               .help("Sets a custom config file")
                               .takes_value(true))
                          .arg(Arg::with_name("INPUT")
                               .help("Sets the input file to use")
                               .required(true)
                               .index(1))
                          .arg(Arg::with_name("v")
                               .short("v")
                               .multiple(true)
                               .help("Sets the level of verbosity"))
                          .subcommand(SubCommand::with_name("test")
                                      .about("controls testing features")
                                      .version("1.3")
                                      .author("Someone E. <someone_else@other.com>")
                                      .arg(Arg::with_name("debug")
                                          .short("d")
                                          .help("print debug information verbosely")))
                          .get_matches();

    // Gets a value for config if supplied by user, or defaults to "default.conf"
    let config = matches.value_of("config").unwrap_or("default.conf");
    println!("Value for config: {}", config);

    // Calling .unwrap() is safe here because "INPUT" is required (if "INPUT" wasn't
    // required we could have used an 'if let' to conditionally get the value)
    println!("Using input file: {}", matches.value_of("INPUT").unwrap());

    // Vary the output based on how many times the user used the "verbose" flag
    // (i.e. 'myprog -v -v -v' or 'myprog -vvv' vs 'myprog -v'
    match matches.occurrences_of("v") {
        0 => println!("No verbose info"),
        1 => println!("Some verbose info"),
        2 => println!("Tons of verbose info"),
        3 | _ => println!("Don't be crazy"),
    }

    // You can handle information about subcommands by requesting their matches by name
    // (as below), requesting just the name used, or both at the same time
    if let Some(matches) = matches.subcommand_matches("test") {
        if matches.is_present("debug") {
            println!("Printing debug info...");
        } else {
            println!("Printing normally...");
        }
    }

    // more program logic goes here...
}

Lassen wir das Projekt nochmals mit cargo build bauen.

❯ cargo build
    Updating crates.io index
  Downloaded textwrap v0.11.0
  Downloaded unicode-width v0.1.8
  Downloaded vec_map v0.8.2
  Downloaded clap v2.33.3
  Downloaded strsim v0.8.0
  Downloaded atty v0.2.14
  Downloaded ansi_term v0.11.0
  Downloaded 7 crates (282.3 KB) in 0.50s
   Compiling libc v0.2.99
   Compiling unicode-width v0.1.8
   Compiling strsim v0.8.0
   Compiling bitflags v1.3.2
   Compiling vec_map v0.8.2
   Compiling ansi_term v0.11.0
   Compiling textwrap v0.11.0
   Compiling atty v0.2.14
   Compiling clap v2.33.3
   Compiling hello-bnova v0.1.0 (/Users/rschneider/Development/rust/hello-bnova)
    Finished dev [unoptimized + debuginfo] target(s) in 6.98s

Jetzt sollten wir in unserem target/-Verzeichnis ein Binary hello-bnova haben, welches wir als CLI-App ausführen können.

❯ ./target/debug/hello-bnova -V
Hello b-nova! 1.0

Das Grundgerüst von Clap macht noch ein wenig mehr als nur 'Hello bnova!' zu printen, aber grundsätzlich funktioniert die CLI-App bereits. Glückwunsch ! 🍀

Auch erwähnenswert sind die unglaublich expressiven und verständliche Fehlermeldungen bei der Kompilation. Ich habe versuchsweise “ durch ' in einer String-Expression ersetzt. Der Kompilier zeigt mir nicht die Linie, sondern schlägt mehrere Lösungswege vor, welche ich oft einfach aus der Fehlermeldung kopieren kann.

❯ cargo build
   Compiling hello-bnova v0.1.0 (/Users/rschneider/Development/rust/hello-bnova)
error: character literal may only contain one codepoint
  --> src/main.rs:46:27
   |
46 |         3 | _ => println!('Don't be crazy')
   |                           ^^^^^
   |
help: if you meant to write a `str` literal, use double quotes
   |
46 |         3 | _ => println!("Don"t be crazy')
   |                           ^^^^^

error[E0762]: unterminated character literal
  --> src/main.rs:46:42
   |
46 |         3 | _ => println!('Don't be crazy')
   |                                          ^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0762`.
error: could not compile `hello-bnova`

To learn more, run the command again with --verbose.

Der Footprint einer CLI-App mit Rust beträgt in diesem Beispiel 3.9 MB, sicher die Hälfte schlanker als ein Gegenstück in Go (mit dem CLI-Framework Cobra).

❯ du -h target/debug/hello-bnova
3.9M	target/debug/hello-bnova

Noch ein paar Worte zu Cargo und Crates

Das Ökosystem von Rust scheint nach gut zehn Jahren Existenz ausgereift und bietet Stand heute Libraries für alle möglichen Use-Cases. Der haus-eigene Package Manager Cargo hat ein eignene Crate-Registry, namentlich https://crates.io/. Darauf findet man alle vorhandenen Crates, die man in einem Rust-Projekt einbauen kann.

An diesem Punkt möchten wir kurz einen kleinen Überblick mit welcher Crate welchen Use-Case abdecken könnte. Eigentlich jeder der unten stehenden Einträge eine eigenes TechUp, da diese in ihrer jeweiligen Kompetenz umfangreich sind. Aber dennoch fassen wir diese hier kurz zusammen.

  • Clap: Clap ist ein CLI-Framework womit man, genau wie mit Cobra bei Go, eine CLI-App programmieren kann.

  • Rayon: Mit Rayon bringt man Parallelität und Nebenläufigkeit in ein Projekt ein.

  • Tokio: Mit Tokio baut man Event-driven, hochverfügbare, asynchrone Applikationen. Tokio ist dabei spezielle auf Leichtgewichtigkeit ausgelegt.

  • Rocket: Rocket ist das to-go Web-Framework für Rust und bietet gewohnte Features, die man von einem Web-Framework erwarten kann.

  • WASM: wasm-pack ist ein WebAssembly-BuildTool, welches aus Rust wasm-Artefakte generieren kann. Seit der Einführung von WebAssembly in 2019 unterstützt jeder Web-Browser die Möglichkeit wasm-Maschinencode auszuführen anstatt eine ECMAScript-kompatible Sprache für Client-seitige Applikationen zu nutzen. Dabei ist Rust, neben Kotlin und Go eine der wenigen Sprachen die bis dato WASM untersützen. Rust ist sich seither als Standard für WASM-Applikationen etabliert.

Fazit

Rust ist eine moderne Sprache, die Dinge etwas anders angeht und sich dabei als optimalen Kandidat für performante und stabile Systementwicklung eignet. Auch wenn vielen Rust noch kein Begriff sein wird und dessen Nutzung sich noch auf marginale Nischenbereiche zu konzentrieren scheint, so hat Rust interessante Eigenschaften, die Rust zur Sprache der Wahl in einem breiten Spektrum von Anwendungsmöglichkeiten macht, dabei nicht zuletzt robuste Web-Services und CLI-Apps.

Wir bei b-nova sind darauf spezialisiert neue Technologien auf Ihre Anwendbarkeit und Nachhaltigkeit zu prüfen und diese, falls unseren Aussichten auf Qualtitäszuwachs entsprechend, auch zum Einsatz und Erfolg von Kundenprojekten zu überführen. Wir sind sicher, dass sich Rust als ein weiteres wertvolles Werkzeug in unserer Toolbox etablieren wird und freuen uns schon heute Rust bei Ihnen in einem Projekt unter Beweis zu stellen. Stay tuned!

Weiterführende Links und Ressourcen

https://www.rust-lang.org/

https://github.com/rust-unofficial/awesome-rust

https://serokell.io/blog/rust-guide

https://tourofrust.com/

https://doc.rust-lang.org/stable/rust-by-example/

Raffael Schneider – crafter, disruptor, free spirit. As a fervent software craftsmanship, Raffael likes to write about programming languages and software resilience in modern distributed systems. Be it DevOps, SRE or systems architecture, he always got a new way of approaching things.