Rust Syntax

6 Kernkonzepte
Die wichtigsten Syntax-Konzepte für fortgeschrittene Rust-Programmierung: Funktionen · Closures · Traits · Generics · Makros · Module Diese Syntax-Elemente sind essenziell für die Entwicklung komplexer und wiederverwendbarer Rust-Anwendungen – von der Fehlerbehandlung bis zur Code-Organisation.

Funktionen – Parameter, Rückgabe, Ausdrücke

fn · -> · return
// Funktion mit Parametern und Rückgabewert fn add(a: i32, b: i32) -> i32 { a + b // impliziter return (Ausdruck) } // Funktion ohne Rückgabewert fn greet(name: &str) { println!("Hallo {}!", name); }

Funktionen sind wiederverwendbare Codeblöcke. Rust-Funktionen können Werte zurückgeben – entweder mit return oder als impliziter Ausdruck (letzte Anweisung ohne Semikolon).

Beispiele
// Funktion mit implizitem Return
fn square(x: i32) -> i32 {
x * x // kein Semikolon = Return
}
// Funktion mit explizitem return
fn divide(a: f64, b: f64) -> f64 {
if b == 0.0 {
return 0.0;
}
a / b
}
// Funktion mit mehreren Rückgabewerten (Tupel)
fn swap(a: i32, b: i32) -> (i32, i32) {
(b, a)
}
let (x, y) = swap(1, 2);
Tipp: Der implizite Return (letzte Ausdruck ohne Semikolon) ist idiomatisch in Rust. Verwenden Sie return nur für frühe Rückgaben (Early Returns).

Closures – Anonyme Funktionen

|param| expr · move · Fn, FnMut, FnOnce
// Einfacher Closure let add = |x, y| x + y; // Closure mit Block let square = |x| { x * x };

Closures sind anonyme Funktionen, die Variablen aus ihrem umgebenden Scope erfassen können. Sie implementieren die Traits Fn, FnMut oder FnOnce.

Beispiele
// Closure mit Umgebungsvariablen
let factor = 2;
let multiply = |x| x * factor;
println!("{}", multiply(5)); // 10
// Closure mit move (Besitz übertragen)
let name = String::from("Rust");
let greet = move || {
println!("Hallo {}!", name);
};
greet();
// Closure als Parameter
fn apply<F>(f: F, x: i32) -> i32
where F: Fn(i32) -> i32 {
f(x)
}
let result = apply(|n| n + 1, 5); // 6
// Closure-Traits
let mut count = 0;
let mut increment = || { count += 1; count }; // FnMut
println!("{}", increment()); // 1
Tipp: Verwenden Sie move bei Closures, wenn Sie den Besitz von erfassten Werten übertragen möchten – z.B. für Threads. Closures ohne move borgen nur.

Traits – Gemeinsames Verhalten

trait · impl · derive · dyn
// Trait definieren trait Summary { fn summarize(&self) -> String; } // Trait implementieren impl Summary for String { fn summarize(&self) -> String { format!("String: {}", self) } }

Traits definieren gemeinsames Verhalten für verschiedene Typen. Sie sind ähnlich wie Interfaces in anderen Sprachen und ermöglichen Polymorphie.

Beispiele
// Trait mit Default-Implementierung
trait Greet {
fn greet(&self) -> String {
String::from("Hallo!")
}
}
// Derive (automatische Implementierung)
#[derive(Debug, Clone, PartialEq)]
struct Person {
name: String,
age: u32,
}
// Trait Bound (Typ muss Trait implementieren)
fn print_summary<T: Summary>(item: &T) {
println!("{}", item.summarize());
}
// dyn (dynamischer Dispatch)
let items: Vec<Box<dyn Summary>> = vec![
Box::new(String::from("Rust")),
Box::new(42), // Wenn i32 Summary implementiert
];
Tipp: Verwenden Sie #[derive] für häufige Traits wie Debug, Clone, PartialEq – das spart Boilerplate-Code.

Generics – Typen flexibel machen

<T> · impl<T> · where
// Generische Funktion fn identity<T>(value: T) -> T { value } // Generische Struktur struct Box<T> { value: T, }

Generics ermöglichen die Erstellung von wiederverwendbaren Code mit verschiedenen Typen. Sie sind eine der Säulen der Rust-Typsicherheit.

Beispiele
// Generische Funktion mit Trait Bound
fn max<T: PartialOrd>(a: T, b: T) -> T {
if a > b { a } else { b }
}
// where-Syntax (lesbarer für mehrere Bounds)
fn process<T, U>(a: T, b: U) -> String
where
T: Debug + Clone,
U: Display,
{ /* ... */ }
// Generische Enum
enum Option<T> {
Some(T),
None,
}
// Generic mit impl (Methoden)
impl<T> Box<T> {
fn new(value: T) -> Box<T> {
Box { value }
}
}
Tipp: Verwenden Sie die where-Syntax bei mehreren Trait Bounds – das verbessert die Lesbarkeit. Generische Parameter werden in <> deklariert.

Makros – Code-Generierung

macro_rules! · println! · vec!
// Einfaches Makro macro_rules! say_hello { () => { println!("Hallo!"); }; }

Makros erzeugen Code zur Kompilierzeit. Sie sind mächtiger als Funktionen, da sie mit der Syntax arbeiten können. Bekannte Makros sind println!, vec! und assert!.

Beispiele
// Makro mit Parametern
macro_rules! greet {
($name:expr) => {
println!("Hallo {}!", $name);
};
}
greet!("Rust"); // Hallo Rust!
// Makro mit variabler Anzahl Argumente
macro_rules! sum {
($(x:expr),*) => {
0 $(+ $x)*
}
}
let result = sum!(1, 2, 3); // 6
// Makro mit verschiedenen Pattern
macro_rules! assert_eq {
($left:expr, $right:expr) => {
if $left != $right {
panic!("Assertion failed: {} != {}", $left, $right);
}
};
}
Tipp: Makros sind ein fortgeschrittenes Feature. Sie sind ideal für Domain-Specific Languages (DSLs) und Code, der zur Compile-Zeit generiert werden muss. In vielen Fällen reichen Funktionen aus.

Module & Use – Code-Organisation

mod · pub · use · crate
// Module-Deklaration mod math; // In math.rs pub fn add(a: i32, b: i32) -> i32 { a + b } // Hauptdatei use crate::math::add;

Module organisieren Code in logische Einheiten. Mit pub werden Elemente nach außen sichtbar. use importiert Elemente in den Scope.

Beispiele
// Datei: lib.rs
mod utils {
pub fn format(s: &str) -> String {
s.to_uppercase()
}
}
// Datei: main.rs
mod utils; // Modul einbinden
use crate::utils::format;
// Verschachtelte Module
mod data {
pub mod config {
pub const MAX: u32 = 100;
}
}
use crate::data::config::MAX;
// re-export
pub use data::config;
// externe Crates
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
Tipp: Die Modul-Hierarchie entspricht der Dateisystem-Struktur. use kann mit geschweiften Klammern mehrere Importe gruppieren: use std::io::{self, Write};

Rust Syntax im Überblick

fn Funktionen
fn add(x: i32) -> i32
|| Closures
|x| x + 1
trait Traits
trait Summary { ... }
<T> Generics
fn foo<T>(x: T)
macro Makros
macro_rules! { ... }
mod Module
mod utils; use crate::utils;

Quick Summary

fn
Funktionen
||
Closures
trait
Traits
<T>
Generics
!
Makros
mod
Module
fn add<T: Add>(a: T, b: T) -> T { a + b } · let f = |x| x * 2; · trait Display { fn fmt(&self) -> String; }