Ferris Talk #15: Bedingte Kompilierung in Rust

Seite 2: Features im Zusammenspiel mit Cargo

Inhaltsverzeichnis

Im Beispiel hat der Rust-Compiler rustc den Code übersetzt. In der Praxis werden jedoch die meisten den Paketmanager Cargo verwenden, statt rustc direkt aufzurufen. In Verbindung mit Cargo ist es üblich, statt der zuvor gezeigten, einfachen Konfigurationseinstellungen [features] zu verwenden, die in der Datei Cargo.toml definiert sind. Sie lassen sich einerseits mit bedingter Kompilierung nutzen, um Codeabschnitte zu aktivieren oder zu deaktivieren. Andererseits können sie Abhängigkeiten steuern. Häufig sind einige Dependencies zu Crates nur dann erforderlich, wenn ein gewisses Feature aktiv ist.

Folgendes Beispiel demonstriert die bedingte Kompilierung. Cargo.toml modelliert die drei Varianten unseres Salates als Features. Außerdem legt sie fest, dass standardmäßig (default) keine Variante aktiv ist. Zusätzlich enthält sie das feature all, um zu demonstrieren, wie sich mehrere Features unter einem Namen kombinieren lassen.

[package]
name = "salad_maker_with_features"
version = "0.1.0"
edition = "2021"

[features]
default = []
traditional = []
vegetarian = []
vegan = []
all = ["traditional", "vegetarian", "vegan"]

[dependencies]

Der folgende Rust-Code zeigt die bedingte Kompilierung auf Basis von Features. Um das Beispiel übersichtlich zu halten, fehlen Codeteile, die nicht zusätzlich zum Verständnis beitragen. Der vollständige Code findet sich auf GitHub.

fn main() {
  // Note that we are now checking cargo features
  #[cfg(not(any(feature = "traditional", 
                feature = "vegetarian", 
                feature = "vegan")))]
  {
    println!(r"You haven't specified whether the
recipe should be traditional, vegetarian, or vegan.");
    panic!();
  }

  #[cfg(any(all(feature = "traditional", 
                feature = "vegetarian"), 
            all(feature = "traditional", 
                feature = "vegan"), 
            all(feature = "vegetarian", 
                feature = "vegan")))]
  {
    println!(r"You've specified more than one
recipe type. Please specify only one.");
    panic!();
  }

  print_ingredients();
  print_preparation();
}

fn print_ingredients() {
  println!("Ingredients for Caesar Salad:");
    
    [...]

  // We combine config predicates again
  #[cfg(any(feature = "traditional", 
            feature = "vegetarian"))]
  println!("- Parmesan cheese");
  #[cfg(any(feature = "vegan"))]
  println!("- Vegan Parmesan cheese substitute");

  #[cfg(feature = "traditional")]
  {
      println!("- Anchovies");
      println!("- Egg yolks");
  }

  [...]
}

fn print_preparation() {
  println!("\nPreparation:");

  println!(r"1. Wash and tear the romaine
lettuce into bite-size pieces.");
  println!(r"2. In a bowl, combine lemon juice,
olive oil, salt, and pepper.");
    
  match (cfg!(feature = "traditional"), 
         cfg!(feature = "vegetarian"), 
         cfg!(feature = "vegan")) {
    (true, false, false) 
      => println!(r"3. Add grated Parmesan cheese,
anchovies, and egg yolks to the bowl."),
    (false, true, false) 
      => println!(r"3. Add grated Parmesan cheese, 
capers, and egg yolks to the bowl."),
    (false, false, true) => 
       println!(r"3. Add vegan Parmesan cheese
substitute, capers, and vegan mayonnaise to the bowl."),
    _ => panic!(r"More than one recipe type? 
This should never happen!"),
  };

  [...]
}

Beim Bearbeiten des Codes verwenden der Rust Analyzer und damit auch Visual Studio Code das Feature default, das im Szenario bewusst fehlt. Die automatische Auswahl erschwert die Entwicklungsarbeit, da der Editor Code, der korrekt und aktiv ist, als inaktiv markiert. Der Grund ist die fehlende Featureauswahl, die zwar vor dem Kompilieren erfolgt, beim Editieren aber noch fehlt. Die folgende Abbildung zeigt, wie der Beispielcode in Visual Studio Code aussieht:

Ohne Featureauswahl zeigt Visual Studio Code den Code im [code]cfg[/code]-Block als inaktiv dar (Abb. 1).

Zum Glück lässt sich dieses Verhalten korrigieren: Die Einstellung rust-analyzer.cargo.features teilt Visual Studio Code mit, welches Feature man für den Rust Analyzer verwenden möchte. Die folgende Abbildung zeigt, wie sich die Visualisierung des aktiven Codes im Editor verändert, wenn man das Feature vegan aktiviert:

Bei der Auswahl eines Features hebt Visual Studio Code den zugehörigen Block als aktiv hervor (Abb. 2).