Cargo-Features und bedingte Kompilierung (#[cfg]) in Rust
Stell dir vor, du bestellst eine Pizza. Jede Pizza bekommt den Teig und die Tomatensoße. Dann wählst du Extras: Pilze, extra Käse, Oliven. Die Küche macht nur, was du bestellt hast. Du zahlst (und wartest) also nicht für Beläge, die du gar nicht willst.
Rust-Code funktioniert genauso. Mit bedingter Kompilierung (#[cfg]) sagst du dem Compiler: „Bau diesen Teil nur unter Windows“ oder „nur in Tests“. Mit Cargo-Features (optionalen Funktionen) suchen sich die Leute, die dein Crate nutzen, die Extras aus, die sie brauchen – zum Beispiel JSON-Unterstützung. Code, den niemand auswählt, wird gar nicht erst kompiliert. Er bremst den Build nicht aus und landet nicht im fertigen Programm.
Diese Seite baut auf Cargo und Crates (Cargo.toml, cargo add) und Rust-Werkzeuge und Einrichtung auf. Wenn dir diese Begriffe neu sind, schau dort zuerst kurz vorbei.
#[cfg]: Code behalten oder entfernen
Abschnitt betitelt „#[cfg]: Code behalten oder entfernen“#[cfg(...)] ist ein Attribut. Du setzt es über eine Funktion, ein Struct, eine use-Zeile, ein Modul oder sogar eine einzelne Anweisung. Ist die Bedingung wahr, bleibt der Code. Ist sie falsch, entfernt der Compiler ihn komplett – so, als hättest du ihn nie geschrieben.
#[cfg(target_os = "linux")]fn os_name() -> &'static str { "Linux"}
#[cfg(target_os = "windows")]fn os_name() -> &'static str { "Windows"}
#[cfg(target_os = "macos")]fn os_name() -> &'static str { "macOS"}
fn main() { println!("Hallo von {}!", os_name());}Ausgabe:
Hallo von Linux!Der Rust Playground läuft unter Linux, deshalb gibt er Hallo von Linux! aus. Auf deinem eigenen Windows-Laptop gibt derselbe Code Hallo von Windows! aus. Drei Funktionen haben denselben Namen, aber das ist in Ordnung: Auf jedem Rechner bleibt nur eine davon übrig.
Hier lauert eine kleine Falle. Unter FreeBSD ist keine der drei Bedingungen wahr. Dann gibt es os_name gar nicht, und der Build bricht mit E0425 ab: „cannot find function os_name in this scope“ (die Funktion os_name wurde in diesem Gültigkeitsbereich nicht gefunden). In den nächsten Abschnitten siehst du, wie du einen Fallback einbaust.
cfg!(): einfach wahr oder falsch
Abschnitt betitelt „cfg!(): einfach wahr oder falsch“Manchmal willst du keinen Code entfernen. Du willst nur eine Frage stellen. Das Makro cfg!() nimmt dieselben Bedingungen, gibt dir aber ein ganz normales bool zurück:
fn main() { let bits = if cfg!(target_pointer_width = "64") { 64 } else { 32 }; println!("Das ist ein {bits}-Bit-Programm"); println!("Der größte usize ist {}", usize::MAX);
if cfg!(target_os = "windows") { println!("Pfade sehen so aus: C:\\Users\\ana"); } else { println!("Pfade sehen so aus: /home/ana"); }}Ausgabe:
Das ist ein 64-Bit-ProgrammDer größte usize ist 18446744073709551615Pfade sehen so aus: /home/anatarget_pointer_width ist die Größe einer Speicheradresse: "64" auf fast jedem modernen Rechner, "32" auf manchen kleinen Geräten. Das ist auch die Größe von usize. Deshalb ist usize::MAX hier so riesig.
Wann nimmst du also was?
#[cfg(...)] |
cfg!(...) |
|
|---|---|---|
| Was es macht | Entfernt den Code, wenn die Bedingung falsch ist | Wird zu true oder false |
| Wird der „falsche“ Code trotzdem kompiliert? | Nein, er ist weg | Ja, beide Zweige müssen überall kompilieren |
| Nimm es für | Code, der woanders nicht kompilieren kann (APIs für nur ein Betriebssystem, optionale Crates) | Kleine Unterschiede im Verhalten |
Die mittlere Zeile ist wichtig. Dinge, die es nur unter Unix gibt, wie std::os::unix::fs::PermissionsExt, existieren unter Windows nicht. Code, der sie nutzt, muss deshalb hinter #[cfg(unix)] stehen. Ein if cfg!(unix) reicht nicht, denn der Compiler unter Windows würde diesen Zweig trotzdem kompilieren wollen.
Bedingungen kombinieren: not, any, all
Abschnitt betitelt „Bedingungen kombinieren: not, any, all“Aus kleinen Bedingungen kannst du größere bauen:
not(a): wahr, wennafalsch istany(a, b, ...): wahr, wenn mindestens eine wahr ist (wie „oder“)all(a, b, ...): wahr, wenn alle wahr sind (wie „und“)
Hier ist noch einmal das Beispiel mit den Betriebssystemen, diesmal mit einem Fallback, damit es überall kompiliert:
#[cfg(any(target_os = "linux", target_os = "macos"))]fn shell() -> &'static str { "sh"}
#[cfg(target_os = "windows")]fn shell() -> &'static str { "cmd"}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]fn shell() -> &'static str { "unbekannt"}
fn main() { println!("Standard-Shell: {}", shell());
if cfg!(all(unix, target_pointer_width = "64")) { println!("Ein 64-Bit-Unix-System"); }}Ausgabe:
Standard-Shell: shEin 64-Bit-Unix-Systemunix und windows sind praktische Kurznamen. unix ist wahr unter Linux, macOS, den BSDs und ähnlichen Systemen. Diesen Bedingungen begegnest du am häufigsten:
| Bedingung | Wahr, wenn … |
|---|---|
target_os = "linux" / "windows" / "macos" |
du für dieses Betriebssystem baust |
unix / windows |
du für ein Unix-artiges System / für Windows baust |
target_arch = "x86_64" / "aarch64" / "wasm32" |
du für diese Art von Prozessor baust (WebAssembly ist wasm32) |
target_pointer_width = "64" |
Adressen (und usize) 64 Bit groß sind |
debug_assertions |
es ein Debug-Build ist (cargo build), kein --release |
test |
du gerade cargo test ausführst |
feature = "json" |
das Cargo-Feature json eingeschaltet ist (mehr dazu weiter unten) |
Du willst die komplette Liste für deinen Rechner sehen? Führ rustc --print cfg aus. Auf einem 64-Bit-Linux-PC gibt das Zeilen wie target_os="linux", target_family="unix" und debug_assertions aus. #[cfg(windows)] und #[cfg(unix)] siehst du oft in FFI-Code, weil sich C-Bibliotheken zwischen den Systemen häufig unterscheiden.
#[cfg(test)] und #[cfg_attr]
Abschnitt betitelt „#[cfg(test)] und #[cfg_attr]“Eine Bedingung hast du schon benutzt, ohne darüber nachzudenken. Jedes Testmodul auf der Seite Testen beginnt mit #[cfg(test)]:
pub fn add_points(total: u32, points: u32) -> u32 { total + points}
#[cfg(test)]mod tests { use super::*;
#[test] fn adds_points() { assert_eq!(add_points(10, 5), 15); }}cargo test schaltet test ein. Dann wird das Modul gebaut und seine Tests laufen. Ein normales cargo build lässt das ganze Modul weg, dein echtes Programm enthält also keinen Testcode. #[cfg] funktioniert so bei jedem Modul (siehe Module), zum Beispiel #[cfg(windows)] mod windows_paths;.
#[cfg_attr(bedingung, attribut)] bedeutet: „Wenn die Bedingung wahr ist, füge dieses Attribut hinzu.“ Ein typischer Fall ist ein Derive, das du nur manchmal haben willst:
#[cfg_attr(debug_assertions, derive(Debug))]struct Player { name: String, score: u32,}
fn main() { let player = Player { name: "Ana".to_string(), score: 120 };
// nur in Debug-Builds; landet auf stderr, nicht auf stdout #[cfg(debug_assertions)] eprintln!("debug: {:?}", player);
println!("{} hat {} Punkte", player.name, player.score);}Ausgabe:
Ana hat 120 PunkteIn einem Debug-Build (der Standard im Playground) siehst du zusätzlich debug: Player { name: "Ana", score: 120 } im Bereich „Standard Error“ der Ausgabe. Stell den Playground auf „Release“ um, dann verschwindet diese Zeile – und mit ihr der Debug-Code für Player. Beachte, dass das eprintln! ein eigenes #[cfg(debug_assertions)] braucht. Ohne das schlägt ein Release-Build fehl, weil Player dort Debug nicht implementiert. Mehr über Debug- und Release-Builds findest du unter Cargo und Crates.
Cargo-Features in deinem eigenen Crate
Abschnitt betitelt „Cargo-Features in deinem eigenen Crate“Jetzt zur Speisekarte. Ein Feature ist ein Schalter mit Namen, den du in Cargo.toml in einer Tabelle [features] festlegst. Bauen wir ein kleines Programm, pizza_order, mit zwei Features:
extra_cheese: ein kostenloses Extra, standardmäßig eingeschaltet.json: gibt die Bestellung als JSON aus. Dafür braucht es die Cratesserdeundserde_json. Sie werden also nur heruntergeladen und kompiliert, wenn jemandjsonanfordert.
[package]name = "pizza_order"version = "0.1.0"edition = "2024"
[dependencies]serde = { version = "1", features = ["derive"], optional = true }serde_json = { version = "1", optional = true }
[features]default = ["extra_cheese"]extra_cheese = []json = ["dep:serde", "dep:serde_json"]Zeile für Zeile:
optional = trueheißt: „Nutze dieses Crate nur, wenn ein Feature danach fragt.“extra_cheese = []legt ein Feature an, das sonst nichts einschaltet. Es ist nur ein Schalter, den dein Code abfragen kann.json = ["dep:serde", "dep:serde_json"]sagt: Wennjsoneingeschaltet ist, schalte auch diese beiden optionalen Abhängigkeiten ein.dep:bedeutet: „Das ist eine Abhängigkeit, kein Feature-Name.“default = [...]listet die Features auf, die eingeschaltet sind, solange niemand etwas anderes sagt.
Im Code fragst du ein Feature mit feature = "name" ab:
#[cfg_attr(feature = "json", derive(serde::Serialize))]struct Order { size: String, toppings: Vec<String>,}
fn main() { let mut toppings = vec!["Tomate".to_string(), "Basilikum".to_string()];
if cfg!(feature = "extra_cheese") { toppings.push("extra Käse".to_string()); }
let order = Order { size: "große".to_string(), toppings }; println!("Bestellung: {} Pizza mit {}", order.size, order.toppings.join(", "));
#[cfg(feature = "json")] println!("Als JSON: {}", serde_json::to_string(&order).unwrap());}Die JSON-Zeile nutzt #[cfg], nicht cfg!. Ohne das Feature json ist serde_json überhaupt keine Abhängigkeit, also muss diese Zeile komplett verschwinden. Die Käse-Abfrage kompiliert in jedem Fall, da reicht cfg!.
Jetzt bestellst du deine Pizza über die Kommandozeile. (-q blendet nur Cargos „Compiling…“-Zeilen aus.)
cargo run -q # nur die Standard-Featurescargo run -q --features json # Standard-Features + jsoncargo run -q --no-default-features # keine Extras, nicht mal die Standard-Featurescargo run -q --all-features # alle FeaturesDas ist die echte Ausgabe:
$ cargo run -qBestellung: große Pizza mit Tomate, Basilikum, extra Käse
$ cargo run -q --features jsonBestellung: große Pizza mit Tomate, Basilikum, extra KäseAls JSON: {"size":"große","toppings":["Tomate","Basilikum","extra Käse"]}
$ cargo run -q --no-default-featuresBestellung: große Pizza mit Tomate, Basilikum
$ cargo run -q --all-featuresBestellung: große Pizza mit Tomate, Basilikum, extra KäseAls JSON: {"size":"große","toppings":["Tomate","Basilikum","extra Käse"]}Du kannst die Optionen kombinieren. --no-default-features --features json gibt dir JSON ohne den Käse. Mehrere Features trennst du mit Kommas: --features json,extra_cheese. Dieselben Optionen funktionieren auch bei cargo build, cargo check und cargo test.
Features deiner Abhängigkeiten einschalten
Abschnitt betitelt „Features deiner Abhängigkeiten einschalten“Meistens stehst du auf der anderen Seite der Theke: Du nutzt Crates, die Features haben. Das hast du schon unter Cargo und Crates gesehen:
[dependencies]serde = { version = "1", features = ["derive"] }tokio = { version = "1", features = ["full"] }serde_json = { version = "1", features = ["preserve_order"] }- Das Feature
derivevonserdegibt dir#[derive(Serialize, Deserialize)]. tokiohat keine Standard-Features. Für sich allein bringt es dir fast nichts.fullschaltet alles ein: die Runtime,#[tokio::main], Timer, Netzwerk, Dateien und mehr. Zum Lernen ist das bequem. In einem echten Projekt kannst du weniger anfordern, zum Beispielfeatures = ["rt-multi-thread", "macros"].preserve_ordervonserde_jsonbehält die Schlüssel eines Objekts in der Reihenfolge, in der du sie eingefügt hast.
cargo add schreibt diese Zeilen für dich: cargo add tokio --features full.
Warum teilen Crates sich so auf? Weil jeder Teil, den du nicht nutzt, Code ist, den du nicht herunterladen, kompilieren oder ausliefern musst. Ein großes Crate wie tokio würde jedes kleine Projekt langsam bauen lassen, wenn es immer alles kompilieren würde. Features halten Kompilierzeiten und Programmgrößen klein.
Du kannst die Standard-Features eines Crates auch abschalten und dir nur das aussuchen, was du brauchst – mit default-features = false:
[dependencies]serde_json = { version = "1", default-features = false, features = ["alloc"] }Welche Features in deinem Projekt eingeschaltet sind, verrät dir Cargo:
cargo tree -e features --features json --depth 1Für pizza_order gibt das aus:
pizza_order v0.1.0 (/home/ana/pizza_order)├── serde feature "default"├── serde feature "derive"└── serde_json feature "default"Ohne --depth 1 siehst du den ganzen Baum, auch die Features, die deine Abhängigkeiten in ihren Abhängigkeiten einschalten. Das ist lang, aber das beste Werkzeug, wenn du dich fragst: „Warum wird dieses Crate überhaupt kompiliert?“
Faustregeln
Abschnitt betitelt „Faustregeln“Features sollten nur etwas hinzufügen. Ein Feature einzuschalten sollte nie eine Funktion entfernen oder ändern, was eine bestehende Funktion tut. Der Grund: Wenn zwei Crates in deinem Projekt serde_json nutzen und eines preserve_order anfordert, das andere aber nicht, kompiliert Cargo serde_json nur einmal – mit den Features von beiden. Jedes Crate muss also weiter funktionieren, wenn jemand anderes zusätzliche Features einschaltet.
Vermeide Features, die nicht gleichzeitig an sein dürfen. Ein Design wie „Feature postgres ODER Feature sqlite, aber nie beide“ geht kaputt, sobald zwei Crates unterschiedliche wählen. Und --all-features funktioniert dann auch nicht mehr. Besser sind Features, die alle gleichzeitig an sein können.
Dokumentiere deine Features. Schreib eine kurze Liste in deine README oder in die Doku deines Crates: den Namen des Features, was es hinzufügt und ob es standardmäßig an ist. Auf docs.rs hat jedes Crate eine Seite „Feature flags“ (zum Beispiel die von tokio), und viele Crates markieren Elemente mit „Available on crate feature … only“ (nur mit dem Feature … verfügbar).
Prüf jede Kombination in der CI. Code hinter einem Feature wird nur kompiliert, wenn dieses Feature an ist. Nimm diese Befehle in deine CI auf, damit sich dort nichts versteckt:
cargo check --all-featurescargo check --no-default-featurescargo test --all-featuresBei größeren Crates kann das Tool cargo-hack jedes Feature einzeln prüfen (cargo hack check --each-feature).
Häufige Fehler
Abschnitt betitelt „Häufige Fehler“Ein Tippfehler im Feature-Namen. Wenn du #[cfg(feature = "jsno")] schreibst, kompiliert der Code trotzdem. Die JSON-Zeile wird nur nie ausgegeben, selbst mit --features json nicht. Zum Glück warnt dich aktuelles Rust:
warning: unexpected `cfg` condition value: `jsno` --> src/main.rs:17:11 |17 | #[cfg(feature = "jsno")] | ^^^^^^^^^^------ | | | help: there is a expected value with a similar name: `"json"` | = note: expected values for `feature` are: `default`, `extra_cheese`, and `json` = help: consider adding `jsno` as a feature in `Cargo.toml` = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by defaultDie Warnung sagt: Der Wert jsno ist unerwartet, es gibt einen mit ähnlichem Namen ("json"), und sie zählt die Features auf, die es wirklich gibt. Ignorier sie nicht. Auf der Kommandozeile ist Cargo strenger: cargo run --features jsno bricht mit error: the package 'pizza_order' does not contain this feature: jsno ab (das Paket hat dieses Feature nicht) und schlägt json vor.
Den Namen der Abhängigkeit statt des Features abfragen. Wegen dep:serde gibt es in pizza_order kein Feature namens serde. Schreibst du #[cfg_attr(feature = "serde", ...)], bekommst du dieselbe Warnung „unexpected cfg condition value“, diesmal für serde. Und dann schlägt cargo run --features json mit E0277 fehl: „the trait bound Order: serde::Serialize is not satisfied“ (Order implementiert serde::Serialize nicht), weil das Derive nie hinzugefügt wurde. Nimm den Namen aus [features]: feature = "json".
dep: vergessen. Sobald irgendein Feature dep:serde sagt, musst du für serde überall dep: verwenden. Füg ein neues Feature yaml = ["serde"] hinzu, und Cargo weigert sich, die Datei zu lesen:
error: failed to parse manifest at `/home/ana/pizza_order/Cargo.toml`
Caused by: feature `yaml` includes `serde`, but `serde` is an optional dependency without an implicit feature Use `dep:serde` to enable the dependency.Cargo erklärt dir, dass serde eine optionale Abhängigkeit ohne implizites Feature ist, und bittet dich, dep:serde zu verwenden. Die Lösung ist yaml = ["dep:serde"]. Und wenn du dep: überall weglässt, legt Cargo stillschweigend für jede optionale Abhängigkeit ein öffentliches Feature an (hier würde dann --features serde funktionieren). Das ist der alte Stil. Mit dep: bleibt deine Feature-Liste sauber und bewusst gewählt.
Code, der nie kompiliert wird. Angenommen, du vertippst dich in der Zeile mit #[cfg(feature = "json")] und schreibst serde_json::to_strng. cargo check meldet, dass alles in Ordnung ist, denn diese Zeile wird standardmäßig nicht kompiliert. Erst cargo check --all-features findet den Fehler:
error[E0425]: cannot find function `to_strng` in crate `serde_json` --> src/main.rs:18:42 | 18 | println!("Als JSON: {}", serde_json::to_strng(&order).unwrap()); | ^^^^^^^^ | ::: /home/ana/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.154/src/ser.rs:2245:1 |2245 | / pub fn to_string<T>(value: &T) -> Result<String>2246 | | where2247 | | T: ?Sized + Serialize, | |__________________________- similarly named function `to_string` defined here |help: a function with a similar name exists | 18 | println!("Als JSON: {}", serde_json::to_string(&order).unwrap()); | +Der Compiler findet to_strng in serde_json nicht. Er zeigt dir, wo die ähnlich benannte Funktion to_string definiert ist, und schlägt sie als Korrektur vor. Genau deshalb gehört --all-features in deine CI.
Übungen
Abschnitt betitelt „Übungen“1. Ein Konfigurationsordner für jedes Betriebssystem
Abschnitt betitelt „1. Ein Konfigurationsordner für jedes Betriebssystem“Schreib eine Funktion config_dir(), die zurückgibt, wo eine Notizen-App ihre Einstellungen speichert: "~/.config/notes" unter Linux, "~/Library/Application Support/notes" unter macOS, r"%APPDATA%\notes" unter Windows und "./notes" auf allen anderen Systemen. Gib das Ergebnis in main aus. Tipp: eine Funktion pro Fall mit #[cfg(target_os = "...")] und not(any(...)) für den Fallback.
Lösung anzeigen
#[cfg(target_os = "linux")]fn config_dir() -> &'static str { "~/.config/notes"}
#[cfg(target_os = "macos")]fn config_dir() -> &'static str { "~/Library/Application Support/notes"}
#[cfg(target_os = "windows")]fn config_dir() -> &'static str { r"%APPDATA%\notes"}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]fn config_dir() -> &'static str { "./notes"}
fn main() { println!("Die Einstellungen liegen in {}", config_dir());}Ausgabe:
Die Einstellungen liegen in ~/.config/notesAuf jedem Rechner bleibt genau eine der vier Funktionen übrig, also findet main immer ein config_dir. Der Playground läuft unter Linux, deshalb siehst du den Linux-Pfad. Eine echte App würde ein Crate wie dirs nutzen, um diese Ordner zuverlässig zu finden.
2. Ein optionaler JSON-Export
Abschnitt betitelt „2. Ein optionaler JSON-Export“Du hast ein kleines Programm leaderboard, das Punktestände ausgibt. Füg ein Feature export hinzu (standardmäßig aus), das serde_json einbindet und eine Funktion export_json bereitstellt. Mit --features export soll das Programm die Punktestände zusätzlich als JSON ausgeben. Tipp: Mach serde_json optional, nutze dep:serde_json im Feature und setz #[cfg(feature = "export")] sowohl über die Funktion als auch über die Zeile, die sie aufruft. Im Playground kannst du keine Cargo-Features einstellen. Probier diese Übung deshalb mit cargo new leaderboard auf deinem Rechner aus.
Lösung anzeigen
[package]name = "leaderboard"version = "0.1.0"edition = "2024"
[dependencies]serde_json = { version = "1", optional = true }
[features]export = ["dep:serde_json"]struct Score { name: &'static str, points: u32,}
#[cfg(feature = "export")]fn export_json(scores: &[Score]) -> String { let list: Vec<serde_json::Value> = scores .iter() .map(|s| serde_json::json!({ "name": s.name, "points": s.points })) .collect(); serde_json::Value::Array(list).to_string()}
fn main() { let scores = [ Score { name: "Ana", points: 120 }, Score { name: "Bo", points: 95 }, ];
for s in &scores { println!("{}: {} Punkte", s.name, s.points); }
#[cfg(feature = "export")] println!("Export: {}", export_json(&scores));}$ cargo run -qAna: 120 PunkteBo: 95 Punkte
$ cargo run -q --features exportAna: 120 PunkteBo: 95 PunkteExport: [{"name":"Ana","points":120},{"name":"Bo","points":95}]Es gibt keine Zeile default = [...], also ist export aus, solange du es nicht anforderst. Ohne das Feature wird serde_json nicht einmal heruntergeladen. Die Funktion und der Aufruf brauchen beide #[cfg], weil beide serde_json erwähnen.
Zusammenfassung
Abschnitt betitelt „Zusammenfassung“#[cfg(bedingung)]behält Code nur, wenn die Bedingung wahr ist. Sonst wird er vor dem Kompilieren entfernt.cfg!(bedingung)ist einfach einbool. Beide Zweige müssen trotzdem auf jeder Plattform kompilieren.- Bedingungen kombinierst du mit
not(..),any(..)undall(..). Häufig sindtarget_os,unix/windows,debug_assertionsundtest. #[cfg_attr(bedingung, attribut)]fügt ein Attribut nur hinzu, wenn die Bedingung wahr ist.- Cargo-Features stehen in
[features]. Nutzeoptional = truezusammen mitdep:namefür optionale Crates unddefault = [...]für das, was standardmäßig an ist. - Features sollten nur etwas hinzufügen. Lass
cargo check --all-featuresin der CI laufen und achte aufunexpected_cfgs-Warnungen.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Was passiert mit einer Funktion, die mit #[cfg(target_os = "windows")] markiert ist, wenn du unter Linux baust?
2. Warum reicht if cfg!(unix) { ... } nicht für Code, der eine API nutzt, die es nur unter Unix gibt?
3. Was macht json = ["dep:serde_json"] in Cargo.toml?
4. Du hast aus Versehen #[cfg(feature = "jsno")] geschrieben. Was passiert?
Punkte: 0 / 4
Weiterlesen
Abschnitt betitelt „Weiterlesen“- Features – The Cargo Book (auf Englisch): die komplette Anleitung zu
[features], optionalen Abhängigkeiten,dep:und dazu, wie Features vereinigt werden. - Conditional compilation – The Rust Reference (auf Englisch): alle
cfg-Optionen und die genauen Regeln für#[cfg],#[cfg_attr]undcfg!. - Makro
cfg!– Rust-Standardbibliothek (auf Englisch): wascfg!zurückgibt und wie es sich vom Attribut#[cfg]unterscheidet. - Specifying Dependencies – The Cargo Book (auf Englisch): wie du
features,default-features = falseundoptionalfür jede Abhängigkeit angibst. - cfg – Rust by Example (auf Englisch): kurze Beispiele für
#[cfg]undcfg!direkt nebeneinander.