Slices (Slice, Typen [T] und &[T]) wurden in Rust eingeführt, um einen sicheren und effizienten Zugang zu Teilmengen von Arrays, Vektoren und anderen Elementfolgen zu ermöglichen. Sie vermeiden Allokationen und wiederholte Kopien von Daten, indem sie nur einen "Blick" oder ein Fenster auf einen Teil der Sammlung bieten. Dies unterscheidet sich sowohl von Arrays, deren Größe zur Compile-Zeit festgelegt ist, als auch von dynamischen Sammlungen, die einen Pointer und eine Länge speichern, aber den Speicher besitzen.
Bei der Arbeit mit Arrays und Vektoren in Sprachen ohne strenge Lebensdauerkontrolle treten oft Fehler auf, die zu Überlauf (out of bounds), Speicherlecks und der Verwendung ungültiger Pointer führen. Es ist wichtig sicherzustellen, dass bei der Arbeit mit Teilmengen von Sammlungen keine Kopien erstellt werden und die Speichersicherheit nicht gefährdet wird, was besonders auf Systemebene von Bedeutung ist.
In Rust ist ein Slice ein "Pointer + Länge" auf einen Teil der Daten, der nicht im Besitz des Inhalts ist. Sie sind immer mit einem Lebensdauer verbunden, und der Compiler stellt sicher, dass das Slice nicht länger lebt als das Original (Array, Vec, String). Die gesamte Arbeit mit Slices erfolgt über sichere Zugriffsoperationen, und jeder Überlauf führt im Runtime zu einem Panic.
Beispielcode:
let arr = [1, 2, 3, 4, 5]; let slice = &arr[1..4]; // [2,3,4] Typ: &[i32] let mut vec = vec![10, 20, 30]; let mut_slice: &mut [i32] = &mut vec[..2]; mut_slice[0] = 99; assert_eq!(vec, [99, 20, 30]);
Wichtige Merkmale:
Kann man ein Slice erstellen, das größer ist als das ursprüngliche Array oder Vektor?
Nein. Der Compiler und das Runtime garantieren, dass ein Slice nur innerhalb der zulässigen Indizes der ursprünglichen Daten erstellt werden kann. Ein Versuch, den Bereich zu überschreiten, führt zu einem Panic.
let arr = [1, 2, 3]; let s = &arr[0..4]; // Panic zur Laufzeit
Sind Slices selbständige Besitzer von Speicher?
Nein. Slices sind nur ein "Fenster" auf Daten, sie besitzen keinen Speicher. Ein Versuch, ein Slice aus einer Funktion zurückzugeben, wenn die Quelle lokal ist, führt zu einem Kompilierungsfehler.
fn give_slice() -> &[i32] { let arr = [1,2,3]; &arr[1..] } // Fehler: arr lebt nicht lange genug
Wie unterscheiden sich Slices von Arrays in Rust auf der Typ- und Operationsebene?
Array hat eine feste Länge, die zur Compile-Zeit bekannt ist, und ist vollständig im Stack enthalten. Ein Slice kann jede Länge haben, die dynamisch bestimmt wird, und speichert immer einen Pointer und eine Länge.
let a: [u32; 3] = [1,2,3]; // Array feste Länge let s: &[u32] = &a[..]; // Slice beliebiger Größe
Ein Programmierer gab ein Slice aus einer Funktion zurück, in der ein lokales Array erstellt wurde. Nach dem Verlassen der Funktion wurde das Original gelöscht, und das Slice wurde zu einem "hängenden" Pointer. Dies verursachte einen Bug und sogar einen Absturz.
Vorteile:
Nachteile:
Slices werden immer als Referenz auf externe Daten erstellt, der Besitzer der Daten und das Slice leben gleich lange. Der Compiler garantiert eine enge Beziehung der Lebensdauer zwischen dem Slice und der Quelle.
Vorteile:
Nachteile: