Obwohl ich heute nicht mehr primär als Entwickler tätig bin, verfolge ich technische Konzepte weiterhin mit Interesse. Vor kurzem habe ich ein kleines Experiment in Rust durchgeführt, um ein besseres Gespür für die Sprache zu bekommen. Dabei ging es darum, eine einfache Core- & Plugin-Architektur abzubilden.

Rust bietet durch sein Type-System interessante Ansätze für solche Architekturen. Das Ziel war es zu sehen, wie man eine modulare Struktur aufbaut, bei der ein Kernsystem durch externe Module erweitert werden kann.

Die Umsetzung

1. Der Plugin-Trait

Die Basis bildet ein Trait. Dieser definiert die notwendigen Methoden, die jedes Plugin implementieren muss:

pub trait Plugin {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn process(&self, text: &str) -> String;
}

2. Die Core-Struktur

Der Core verwaltet die Plugins in einem Vektor. Da die Plugins unterschiedliche zugrunde liegende Typen haben können, wird Box<dyn Plugin> verwendet.

pub struct Core {
    plugins: Vec<Box<dyn Plugin>>,
}

impl Core {
    pub fn new() -> Self {
        Core {
            plugins: Vec::new(),
        }
    }

    pub fn register_plugin(&mut self, plugin: Box<dyn Plugin>) {
        println!("Registering plugin: {}", plugin.name());
        self.plugins.push(plugin);
    }
}

3. Implementierung

Für das Experiment wurden verschiedene einfache Plugins erstellt (z.B. WordCounter, CaseConverter). Die Registrierung erfolgt gesammelt:

pub fn register_all_plugins(core: &mut Core) {
    core.register_plugin(Box::new(word_counter::WordCounterPlugin::new()));
    core.register_plugin(Box::new(text_search::TextSearchPlugin::new()));
    core.register_plugin(Box::new(case_converter::CaseConverterPlugin::new()));
}

Praxisnahe Anwendungen

In der Praxis finden solche Architekturen überall dort Anwendung, wo Systeme erweiterbar bleiben müssen, ohne den Kerncode zu verändern. Typische Beispiele sind Texteditoren oder IDEs, die durch Plugins neue Sprachen unterstützen, oder CLI-Tools, die für spezifische Workflows angepasst werden können. Auch in Webservern (als Middleware) oder bei Datenverarbeitungs-Pipelines, in denen verschiedene Filter nacheinander auf einen Datenstrom angewendet werden, ist dieser modulare Aufbau weit verbreitet.

Bekannte Open-Source-Projekte, die auf ähnliche Weise stark auf Modularität setzen, sind zum Beispiel:

  • Nushell: Eine moderne Shell, deren Funktionalität fast vollständig durch Plugins erweitert werden kann.
  • Zellij: Ein Terminal-Workspace, der WebAssembly (WASM) nutzt, um isolierte Plugins zu laden.
  • Bevy: Eine potente Game-Engine, die so konzipiert ist, dass selbst Kernfunktionen als austauschbare Plugins implementiert sind.

Fazit

Der Ansatz ist funktional und zeigt, wie Rust mit Dynamic Dispatch umgeht. Die strikten Vorgaben des Compilers sorgen dafür, dass die Schnittstellen sauber definiert werden müssen. Es war ein nützlicher Versuch, um die Arbeitsweise mit Traits und Box-Typen in Rust besser zu verstehen.