Rust Grundlagen

6 Kernkonzepte
Die wichtigsten Grundlagen der Rust-Programmierung: Syntax · Variablen · Ownership · Borrowing · Kontrollstrukturen · Pattern Matching Rust ist eine systemnahe Sprache, die Speichersicherheit ohne Garbage Collector garantiert. Diese Konzepte sind essenziell für die Entwicklung sicherer und performanter Anwendungen.

Syntax – Grundstruktur von Rust

fn main() · println! · let
fn main() { println!("Hallo Welt!"); let x = 42; println!("x = {}", x); }

Rust-Programme beginnen mit der main()-Funktion. Ausgaben erfolgen mit dem println!-Makro. Variablen werden mit let deklariert und sind standardmäßig unveränderlich (immutable).

Wichtige Syntax-Elemente

Element Beschreibung
fn main() Hauptfunktion (Einstiegspunkt)
println!("text") Ausgabe mit Zeilenumbruch
print!("text") Ausgabe ohne Zeilenumbruch
let x = 5; Variable deklarieren (immutable)
let mut x = 5; Variable deklarieren (mutable)
// Kommentar Einzeiliger Kommentar
/* */ Mehrzeiliger Kommentar
Beispiele
// Grundstruktur
fn main() {
// Variablen (standardmäßig immutable)
let name = "Anna";
let mut alter = 30;
alter = 31; // möglich weil mut
// Ausgabe mit Platzhaltern
println!("Name: {}, Alter: {}", name, alter);
// Typannotation (optional)
let zahl: i32 = 42;
// Konstanten
const MAX: u32 = 100;
}
Tipp: Rust-Variablen sind standardmäßig immutable. Verwenden Sie mut, wenn Sie eine Variable ändern möchten. Das fördert die Sicherheit und Lesbarkeit.

Variablen & Datentypen – int, float, bool, char, String

i32 · f64 · bool · char · String
let a: i32 = 42; // Ganzzahl (32-bit) let b: f64 = 3.14; // Gleitkommazahl let c: bool = true; // Wahrheitswert let d: char = 'A'; // Zeichen let e: String = "Hallo".to_string();

Rust ist eine statisch typisierte Sprache mit starker Typinferenz. Die wichtigsten Datentypen sind Ganzzahlen, Gleitkommazahlen, boolesche Werte, Zeichen und Strings.

Die wichtigsten Datentypen

Typ Größe Bereich Beispiel
i32 4 Byte -2.147.483.648 bis 2.147.483.647 42
u32 4 Byte 0 bis 4.294.967.295 100
i64 8 Byte ±9.22e18 123456789
f64 8 Byte ±1.7e−308 bis ±1.7e+308 3.14159
bool 1 Byte true / false true
char 4 Byte Unicode-Zeichen 'A'
String variabel UTF-8 Text "Hallo".to_string()
&str variabel String-Slice (Referenz) "Hallo"
Beispiele
// Ganzzahlen
let a = 42; // i32 (Typinferenz)
let b: u64 = 100;
// Gleitkommazahlen
let pi = 3.14; // f64
let e: f32 = 2.718;
// Boolean und Char
let ist_wahr = true;
let buchstabe = 'ß'; // Unicode
// String vs &str
let s1 = "Hallo"; // &str (String-Slice)
let s2 = "Welt".to_string(); // String (heap)
let s3 = String::from("Rust");
Tipp: Verwenden Sie &str für Text, der nicht verändert werden muss (effizienter). Verwenden Sie String für veränderlichen Text auf dem Heap.

Ownership – Besitzverwaltung

move · clone · copy
// Ownership-Regeln: // 1. Jeder Wert hat einen Owner. // 2. Es kann nur einen Owner geben. // 3. Wenn der Owner out of scope geht, wird der Wert gelöscht. let s1 = String::from("Hallo"); let s2 = s1; // s1 wird invalidiert (move) // println!("{}", s1); // Fehler!

Ownership ist das zentrale Konzept von Rust für Speichersicherheit. Jeder Wert hat genau einen Besitzer. Bei Zuweisungen oder Übergaben an Funktionen wird der Besitz übertragen (move).

Beispiele
// Move (Besitzübertragung)
let s1 = String::from("Rust");
let s2 = s1; // s1 moved zu s2
// Clone (tiefe Kopie)
let s3 = String::from("Hallo");
let s4 = s3.clone(); // s3 bleibt gültig
println!("{} {}", s3, s4); // beide gültig
// Copy-Typen (kein Move)
let x = 5; // i32 implementiert Copy
let y = x; // x bleibt gültig
println!("x = {}, y = {}", x, y);
// Ownership in Funktionen
fn take_ownership(s: String) { /* ... */ }
let text = String::from("test");
take_ownership(text); // text moved
// Rückgabe von Ownership
fn create_string() -> String {
String::from("neu")
}
let s = create_string(); // Ownership übertragen
Tipp: Verwenden Sie clone(), wenn Sie eine tiefe Kopie benötigen. Für einfache Typen (die das Copy-Trait implementieren) erfolgt keine Move-Operation.

Borrowing & Referenzen – & · &mut · &str

&T · &mut T · Referenzen
// Unveränderliche Referenz (Borrow) let s = String::from("Hallo"); let len = calculate_len(&s); // s wird ausgeliehen // Veränderliche Referenz let mut s2 = String::from("Welt"); change(&mut s2);

Borrowing ermöglicht den Zugriff auf einen Wert, ohne den Besitz zu übernehmen. Es gibt unveränderliche (&T) und veränderliche (&mut T) Referenzen. Regeln: Entweder mehrere unveränderliche oder eine veränderliche Referenz.

Beispiele
// Unveränderliche Referenz
fn print_len(s: &str) {
println!("Länge: {}", s.len());
}
let text = "Rust";
print_len(text); // &str wird übergeben
// Veränderliche Referenz
fn append_world(s: &mut String) {
s.push_str(" Welt");
}
let mut s = String::from("Hallo");
append_world(&mut s);
// Regeln für Referenzen
let mut data = String::from("Test");
let r1 = &data; // unveränderlich
let r2 = &data; // mehrere unveränderlich erlaubt
// let r3 = &mut data; // Fehler! (keine veränderliche Referenz neben unveränderlichen)
let r3 = &mut data; // nachdem r1, r2 out of scope sind
Tipp: Referenzen ermöglichen es, Werte zu nutzen, ohne den Besitz zu übertragen. Das ist effizient und sicher. Achten Sie auf die Borrow-Checker-Regeln – sie verhindern Data Races.

Kontrollstrukturen – if, loop, while, for

if · else · loop · while · for
if x > 0 { println!("Positiv"); } else { println!("Negativ oder Null"); } for i in 0..5 { println!("{}", i); }

Kontrollstrukturen in Rust ähneln denen anderer Sprachen, haben aber einige Besonderheiten. if kann auch einen Wert zurückgeben. loop ist eine Endlosschleife, while und for arbeiten mit Bedingungen bzw. Iteratoren.

Beispiele
// if als Ausdruck (gibt Wert zurück)
let x = 10;
let status = if x > 0 { "positiv" } else { "nicht positiv" };
// loop (Endlosschleife mit break)
let mut count = 0;
loop {
count += 1;
if count == 3 { break; }
}
// while
let mut n = 3;
while n > 0 {
println!("{}", n);
n -= 1;
}
// for über Bereich
for i in 0..5 { // 0 bis 4
println!("{}", i);
}
// for über Collection
let namen = vec!["Anna", "Ben", "Clara"];
for name in &namen {
println!("{}", name);
}
Tipp: Verwenden Sie for mit range (0..n) für zählergesteuerte Schleifen. Das ist idiomatischer und sicherer als while mit manuellem Zähler.

Pattern Matching – match & destructuring

match · if let · while let
match x { 0 => println!("Null"), 1 | 2 => println!("Eins oder Zwei"), n => println!("Andere: {}", n), }

Pattern Matching ist ein mächtiges Feature in Rust. Mit match können Sie Werte auf verschiedene Muster abgleichen. if let und while let sind kompakte Alternativen für spezifische Muster.

Beispiele
// match mit Enum
enum Color {
Red,
Green,
Blue,
}
let color = Color::Red;
match color {
Color::Red => println!("Rot"),
Color::Green => println!("Grün"),
Color::Blue => println!("Blau"),
}
// match mit Option
let maybe_value: Option<i32> = Some(42);
match maybe_value {
Some(value) => println!("Wert: {}", value),
None => println!("Kein Wert"),
}
// if let (kompakt für einen Fall)
let number = Some(10);
if let Some(n) = number {
println!("Zahl: {}", n);
}
// Destructuring (Tupel)
let point = (3, 4);
match point {
(0, 0) => println!("Ursprung"),
(x, 0) => println!("X-Achse: {}", x),
(0, y) => println!("Y-Achse: {}", y),
(x, y) => println!("({}, {})", x, y),
}
Tipp: match muss vollständig sein (alle Fälle abdecken). Verwenden Sie _ als Catch-All für alle nicht behandelten Fälle. if let ist kompakter, wenn Sie nur einen Fall interessiert.

Rust Grundlagen im Überblick

fn Funktion definieren
fn main() { ... }
let Variable deklarieren
let x = 5;
mut Veränderliche Variable
let mut x = 5;
& Referenz (Borrow)
&T, &mut T
match Pattern Matching
match value { ... }
Option Optionaler Wert
Some(T) oder None

Quick Summary

fn
Funktionen
let
Variablen
move
Ownership
&
Borrowing
if
Kontrollstrukturen
match
Pattern Matching
let x = 5; · let y = &x; · match value { Some(v) => v, None => 0 }