&[T] ist der allgemeine Element-Slice — eine Referenz auf einen zusammenhängenden Bereich von T-Werten beliebiger Länge. Wo &str das Spezial-Format für UTF-8-Strings ist, ist &[T] die generische Form für jede andere Sequenz: &[i32], &[u8], &[Person], &[(K, V)]. Eine Funktion mit &[T]-Parameter akzeptiert Arrays, Vecs, Sub-Bereiche von beidem — über Auto-Deref-Coercion alles transparent. Dieser Artikel zeigt die Range-Syntax in voller Breite, das interne Speicher-Layout, die wichtigsten Methoden und macht klar, warum &[T] der Standard-Funktions-Parameter für Sequenzen ist.

Was &[T] ist

Genau wie &str ist &[T] ein Fat Pointer:

Rust Speicher-Layout
use std::mem::size_of;

fn main() {
    let v: Vec<i32> = vec![10, 20, 30];
    let s: &[i32] = &v;

    println!("{}", size_of::<&[i32]>());       // 16 — Pointer + Länge
    println!("{}", size_of::<&i32>());          // 8 — nur Pointer
    println!("{}", s.len());                     // 3
}

Zwei Felder, je 8 Bytes auf 64-bit:

  • Pointer auf das erste Element.
  • Länge in Anzahl der Elemente (nicht in Bytes — der Compiler weiß die Element-Größe).

Der Slice besitzt die Elemente nicht — er borrowt sie. Die Original-Sammlung (Array, Vec) bleibt der Besitzer.

Wie ein Slice entsteht

Auto-Deref-Coercion von Vec

Rust Coercion
let v: Vec<i32> = vec![1, 2, 3, 4, 5];
let s: &[i32] = &v;             // &Vec<i32> → &[i32] via Deref
// Vec<T> implementiert Deref<Target = [T]>

Auto-Deref-Coercion von Array

Rust Array-Coercion
let a: [i32; 5] = [1, 2, 3, 4, 5];
let s: &[i32] = &a;             // &[i32; 5] → &[i32]

Range-Indexierung

Rust Sub-Slice
let v = vec![10, 20, 30, 40, 50];
let s1: &[i32] = &v[..];        // ganzer Vec als Slice
let s2: &[i32] = &v[..3];        // [10, 20, 30]
let s3: &[i32] = &v[2..];        // [30, 40, 50]
let s4: &[i32] = &v[1..4];       // [20, 30, 40]
let s5: &[i32] = &v[1..=3];      // [20, 30, 40] (inklusiv)

Die Range-Syntax kennt fünf Varianten:

SyntaxBedeutung
..Alles
..nVom Anfang bis Index n (exklusiv)
n..Ab Index n bis Ende
n..mVon n (inklusiv) bis m (exklusiv)
n..=mVon n bis m (beide inklusiv)

Out-of-bounds führt zu Runtime-Panic.

Die wichtigsten Slice-Methoden

Längen und Prüfungen

Rust Basics
let v = [10, 20, 30];
let s: &[i32] = &v;

s.len();                // 3
s.is_empty();           // false
s.first();              // Some(&10)
s.last();               // Some(&30)

Sicherer Zugriff

Rust get
let s = [10, 20, 30];

let a = s[1];                // 20 — Panic bei Out-of-Bounds
let b = s.get(1);            // Some(&20)
let c = s.get(99);           // None — kein Panic
let d = s.get(1..3);         // Some(&[20, 30])

get ist die fallible Variante — gibt Option<&T> zurück. Bei User-Input oder unsicheren Quellen besser als direkter Index-Zugriff.

Iteration

Rust Iteration
let s = [1, 2, 3, 4, 5];

for x in s.iter() {                          // &i32
    print!("{x} ");
}

for (i, x) in s.iter().enumerate() {         // (usize, &i32)
    print!("{i}={x} ");
}

let summe: i32 = s.iter().sum();             // 15
let max = s.iter().max();                    // Some(&5)

Suche

Rust Find
let s = [10, 20, 30, 40, 50];

s.contains(&30);                              // true
s.iter().position(|&x| x > 25);              // Some(2)
s.iter().find(|&&x| x > 25);                  // Some(&30)
s.iter().any(|&x| x > 100);                   // false
s.iter().all(|&x| x > 5);                     // true

// binary_search auf sortiertem Slice
let pos = s.binary_search(&30);              // Ok(2)
let missing = s.binary_search(&25);          // Err(2) — würde an Index 2 eingefügt

binary_search ist O(log n) — funktioniert nur auf sortierten Slices. Err(i) gibt die Position, an der der gesuchte Wert eingefügt werden müsste.

Splitting

Rust split
let s = [1, 2, 0, 3, 4, 0, 5];

// Bei jedem Vorkommen von 0 splitten
let teile: Vec<&[i32]> = s.split(|&x| x == 0).collect();
// [[1, 2], [3, 4], [5]]

// Erste Hälfte / zweite Hälfte
let (links, rechts) = s.split_at(3);
// links = [1, 2, 0], rechts = [3, 4, 0, 5]

// Erstes Element + Rest
if let Some((kopf, schwanz)) = s.split_first() {
    println!("Kopf: {kopf}, Schwanz: {schwanz:?}");
}

Konvertierungen

Rust Convert
let s = [1, 2, 3];

let als_vec: Vec<i32> = s.to_vec();          // alloziert neu
let als_array: [i32; 3] = s.try_into().unwrap();  // wenn Länge passt

API-Pattern: &[T] als Parameter

Wie bei &str gilt: &[T] ist die idiomatische Form für Sequenz-Parameter. Sie akzeptiert alles, was sich zu einem Slice coercen lässt.

Rust Universeller Konsument
pub fn summe(daten: &[i32]) -> i32 {
    daten.iter().sum()
}

fn main() {
    let array = [1, 2, 3, 4];
    let vec = vec![10, 20, 30];

    assert_eq!(summe(&array), 10);
    assert_eq!(summe(&vec), 60);
    assert_eq!(summe(&vec[1..]), 50);
    assert_eq!(summe(&[]), 0);              // leerer Slice
}

Vier verschiedene Aufrufer-Formen, eine einzige Funktions-Signatur. Clippy warnt mit clippy::ptr_arg, wenn du &Vec<T> statt &[T] schreibst.

Slices sind Copy

Eine Slice-Referenz ist Copy (8/16-Byte-Pointer-Bytes):

Rust Slice-Copy
fn main() {
    let v = vec![1, 2, 3];
    let s1: &[i32] = &v;
    let s2 = s1;                // Kopie der Referenz
    let s3 = s1;                // weitere Kopie — s1 weiterhin nutzbar
    assert_eq!(s1.len() + s2.len() + s3.len(), 9);
}

Mehrfache Verwendung in derselben Funktion ohne Borrow-Probleme.

Slices und Lifetimes

Ein Slice ist immer an die Lebenszeit seiner Quelle gebunden:

Rust Lifetime
fn ersten_drei(daten: &[i32]) -> &[i32] {
    &daten[..3.min(daten.len())]
}
// Implizit: fn ersten_drei<'a>(daten: &'a [i32]) -> &'a [i32]

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    let teil = ersten_drei(&v);
    println!("{teil:?}");        // [1, 2, 3]
}

teil lebt nicht länger als v. Wenn v gedroppt würde, wäre teil ungültig — der Compiler verhindert das. Mehr im Lifetimes-Kapitel.

Praxis: &[T] im echten Code

Statistik-Library

Rust Stats
pub fn mittel(werte: &[f64]) -> Option<f64> {
    if werte.is_empty() { return None; }
    let summe: f64 = werte.iter().sum();
    Some(summe / werte.len() as f64)
}

pub fn varianz(werte: &[f64]) -> Option<f64> {
    let m = mittel(werte)?;
    let summe: f64 = werte.iter()
        .map(|v| (v - m).powi(2))
        .sum();
    Some(summe / werte.len() as f64)
}

fn main() {
    let messungen = vec![1.0, 2.0, 3.0, 4.0, 5.0];
    assert_eq!(mittel(&messungen), Some(3.0));
    assert_eq!(varianz(&messungen), Some(2.0));
}

&[f64] als Parameter — keine Allocation, kein Move. Aufrufer kann das gleiche Array für mehrere Berechnungen wiederverwenden.

Lookup-Tabelle

Rust Lookup
pub fn finde_status(
    sortierte_codes: &[(u16, &str)],
    code: u16,
) -> Option<&str> {
    sortierte_codes
        .binary_search_by_key(&code, |&(c, _)| c)
        .ok()
        .map(|i| sortierte_codes[i].1)
}

fn main() {
    let table = [
        (200, "OK"),
        (404, "Not Found"),
        (500, "Internal Server Error"),
    ];
    assert_eq!(finde_status(&table, 404), Some("Not Found"));
    assert_eq!(finde_status(&table, 999), None);
}

binary_search_by_key ist O(log n) und greift auf Tupel-Felder per Closure zu — sehr effizient für sortierte Tabellen.

Network-Buffer-Verarbeitung

Rust Packet-Parse
pub fn ist_magic_pattern(buffer: &[u8]) -> bool {
    buffer.starts_with(&[0xDE, 0xAD, 0xBE, 0xEF])
}

pub fn extrahiere_payload(buffer: &[u8]) -> &[u8] {
    // Erste 4 Bytes sind Magic, dann der Payload
    if buffer.len() < 4 || !ist_magic_pattern(buffer) {
        return &[];
    }
    &buffer[4..]
}

fn main() {
    let pkt = [0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03];
    assert_eq!(extrahiere_payload(&pkt), &[0x01, 0x02, 0x03]);
}

&[u8]-Parameter — funktioniert mit Vec<u8>, [u8; N], oder einem Bereich aus einem größeren Buffer.

Sort mit Custom-Closure

Rust Sortierung
pub fn sortiere_nach_laenge(strings: &mut [String]) {
    strings.sort_by_key(|s| s.len());
}

fn main() {
    let mut v: Vec<String> = vec![
        "ccc".into(),
        "a".into(),
        "bb".into(),
    ];
    sortiere_nach_laenge(&mut v);
    assert_eq!(v, vec!["a".to_string(), "bb".into(), "ccc".into()]);
}

&mut [T] für In-Place-Sortierung. Funktioniert mit Vec, Array und Sub-Slice.

Histogramm-Generator

Rust Histogramm
use std::collections::HashMap;

pub fn haeufigkeiten<T: Eq + std::hash::Hash + Clone>(items: &[T]) -> HashMap<T, u32> {
    let mut counts = HashMap::new();
    for item in items {
        *counts.entry(item.clone()).or_insert(0) += 1;
    }
    counts
}

fn main() {
    let v = vec!["a", "b", "a", "c", "b", "a"];
    let h = haeufigkeiten(&v);
    assert_eq!(h.get("a"), Some(&3));
    assert_eq!(h.get("b"), Some(&2));
}

Generisch über T: Eq + Hash + Clone — funktioniert mit jedem hashbaren Typ.

Median-Berechnung

Rust Median
pub fn median(daten: &[i32]) -> Option<f64> {
    if daten.is_empty() { return None; }
    let mut sortiert = daten.to_vec();
    sortiert.sort();
    let mitte = sortiert.len() / 2;
    if sortiert.len() % 2 == 0 {
        Some((sortiert[mitte - 1] + sortiert[mitte]) as f64 / 2.0)
    } else {
        Some(sortiert[mitte] as f64)
    }
}

to_vec() alloziert eine eigene Kopie — das Original bleibt unverändert.

Erste-N-Werte-Extraktion

Rust Take-N
pub fn top_n<T: Ord + Clone>(daten: &[T], n: usize) -> Vec<T> {
    let mut v = daten.to_vec();
    v.sort_by(|a, b| b.cmp(a));     // absteigend sortieren
    v.into_iter().take(n).collect()
}

fn main() {
    let scores = [42, 15, 87, 9, 64];
    let top3 = top_n(&scores, 3);
    assert_eq!(top3, vec![87, 64, 42]);
}

Image-Pixel-Average

Rust Image-Stats
pub fn durchschnitts_pixel(rgb: &[u8]) -> Option<[u8; 3]> {
    if rgb.len() < 3 || rgb.len() % 3 != 0 {
        return None;
    }
    let mut r: u64 = 0;
    let mut g: u64 = 0;
    let mut b: u64 = 0;
    let pixel_anzahl = (rgb.len() / 3) as u64;
    for chunk in rgb.chunks_exact(3) {
        r += chunk[0] as u64;
        g += chunk[1] as u64;
        b += chunk[2] as u64;
    }
    Some([
        (r / pixel_anzahl) as u8,
        (g / pixel_anzahl) as u8,
        (b / pixel_anzahl) as u8,
    ])
}

chunks_exact(3) iteriert in 3-Byte-Schritten über RGB-Tripel — sehr typisch für Bild-Verarbeitung.

FAQ

Soll ich &[T] oder &Vec nehmen?

Immer &[T]. Funktioniert mit Vec, Array, Sub-Slice — viel flexibler. &Vec<T> schließt Arrays und Sub-Slices aus. Clippy warnt mit clippy::ptr_arg.

Was ist der Unterschied zwischen [T] und &[T]?

[T] ist ein „unsized" Typ — der Compiler kennt seine Größe zur Compile-Zeit nicht. Du kannst keine Variable vom Typ [T] haben. Was du immer hast: eine Referenz (&[T], &mut [T]) oder einen Heap-Wrapper (Box<[T]>, Vec<T>).

iter() liefert &T, into_iter() liefert T.

Bei &[T] ist es immer iter() — du borrowst, du kannst nicht konsumieren. into_iter() würde den Slice verbrauchen, was bei einer Referenz nicht passt. Für konsumierende Iteration: ein Vec besitzen und v.into_iter() aufrufen.

Slice-Indices sind usize.

s[i] erwartet i: usize. Negative Indices gibt es nicht. Für „letztes Element" gibt es s.last() oder s[s.len() - 1].

get(i) ist die fallible Variante von [i].

s.get(i) gibt Option<&T> zurück — None bei Out-of-Bounds. Bei User-Input immer get statt direkter Index-Zugriff, um Panics zu vermeiden.

contains(&x) erwartet eine Referenz.

s.contains(&30) — das & ist nötig, weil contains einen &T-Parameter hat. Bei Strings/Slices oft Verwechslung mit dem normalen Wert.

binary_search braucht sortierten Input.

Die O(log n)-Effizienz funktioniert nur, wenn der Slice sortiert ist. Bei unsortierten Daten gibt es undefiniertes Resultat (nicht UB, aber falsche Ok/Err-Antworten). Sortier mit s.sort() (für Ord-Typen) oder s.sort_by(|a, b| ...).

Slice-Referenzen sind Copy.

Sowohl &[T] als auch &str sind 16 Bytes große, kopierbare Werte. Mehrfach in derselben Funktion zu nutzen, geht ohne Probleme — der Borrow Checker stört nicht.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Slices & Views

Zur Übersicht