Programmiersprachen Rust

Rust - Boxing

In Rust gibt es das Konzept des Boxing, das es ermöglicht, Werte auf dem Heap zu speichern, anstatt auf dem Stack. Boxing wird in Rust durch den Typ Box<T> repräsentiert, der einen Wert von Typ T in einem Box-Objekt verpackt und den Speicher dynamisch auf dem Heap verwaltet. Dies ist besonders nützlich, wenn die Größe des Typs zur Kompilierzeit nicht bekannt ist oder bei rekursiven Datentypen.


3 Minuten Lesezeit
14 Okt 2024
documents/boxer_3P3CI8l.jpg

Was lerne ich in diesem Kurs?

In diesem Tutorial lernst du, wie Boxing in Rust funktioniert, wann es sinnvoll ist, und wie man es effektiv einsetzt.

Boxing ist ein wichtiges Werkzeug in Rust, um Werte auf dem Heap zu speichern und dynamische Datentypen zu verwalten. Ob für rekursive Datenstrukturen, polymorphe Typen oder um den Stack zu entlasten – Box bietet eine einfache Möglichkeit, Speicher effizient zu nutzen und den Rust-typischen Sicherheitsgarantien treu zu bleiben.

1. Was ist Boxing?

In Rust werden die meisten Daten standardmäßig auf dem Stack gespeichert, da dies sehr effizient ist. Der Stack hat jedoch eine feste Größe und ist begrenzt. Der Heap hingegen ist dynamischer und kann größere Datenmengen verwalten. Boxing ermöglicht es, Daten auf den Heap zu verschieben, indem sie in einer Box verpackt werden.

Der Typ Box<T> stellt einen Zeiger auf einen Wert des Typs T dar, der auf dem Heap gespeichert wird. Die Größe der Box selbst ist zur Kompilierzeit bekannt, da sie lediglich die Adresse des gespeicherten Wertes enthält.

Beispiel: Einfaches Boxing

fn main() {
    let x = 5;
    let boxed_x = Box::new(x); // x wird auf den Heap verschoben
    println!("x: {}, boxed_x: {}", x, boxed_x);
}

In diesem Beispiel wird die Variable x mit dem Wert 5 auf dem Stack erstellt. Die Box boxed_x enthält jedoch eine Kopie dieses Werts, der jetzt auf dem Heap gespeichert ist.


2. Wann sollte man Boxing verwenden?

Boxing ist in Rust in verschiedenen Situationen nützlich:

  1. Wenn die Größe des Typs zur Kompilierzeit nicht bekannt ist:
  2. Manche Typen, insbesondere rekursive Strukturen wie Bäume oder Listen, haben eine dynamische Größe. In solchen Fällen kann Box<T> verwendet werden, um sicherzustellen, dass der Wert auf dem Heap gespeichert wird, auch wenn seine Größe nicht statisch bekannt ist.

  3. Speicheroptimierung:

  4. Bei der Arbeit mit großen Datenstrukturen oder in Systemen mit begrenztem Stack-Speicher kann es sinnvoll sein, diese Daten auf den Heap zu verschieben.

  5. Verwendung von dynamischen Typen:

  6. Wenn man polymorphe Typen oder dynamische Dispatch benötigt, kann Box<dyn Trait> verwendet werden, um die Implementierung eines Traits zu referenzieren.

3. Arbeiten mit Box in Rust

Boxing in Rust ist einfach zu verwenden und kann auf verschiedene Arten eingesetzt werden.

Beispiel: Werte auf den Heap verschieben

Um einen Wert zu boxen, kann die Methode Box::new() verwendet werden:

fn main() {
    let my_value = Box::new(42); // Box<T> speichert den Wert 42 auf dem Heap
    println!("Boxed value: {}", my_value);
}

Hier wird der Wert 42 auf dem Heap gespeichert, und die Box selbst wird auf dem Stack platziert.

Dereferenzieren einer Box

Um den Wert aus der Box zu erhalten, verwendet man den Dereferenzierungsoperator (*), ähnlich wie bei Rohzeigern.

fn main() {
    let boxed_value = Box::new(100);
    println!("Boxed value: {}", *boxed_value); // Dereferenzierung der Box
}

Das Dereferenzieren gibt den Wert zurück, der im Heap gespeichert ist.


4. Rekursive Datentypen und Boxing

In Rust können rekursive Datentypen ohne Boxing nicht direkt erstellt werden, da ihre Größe zur Kompilierzeit nicht festgelegt werden kann. Mit Box<T> kann jedoch sichergestellt werden, dass der Speicher für rekursive Datenstrukturen dynamisch verwaltet wird.

Beispiel: Rekursive Liste

Hier ist ein Beispiel einer rekursiven Liste, die mit Box<T> implementiert ist:

enum List {
    Cons(i32, Box<List>), // Kons-Struktur mit einem Wert und einer Box auf den nächsten Knoten
    Nil,                  // Ende der Liste
}

fn main() {
    let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Cons(3, Box::new(List::Nil))))));

    println!("Liste erstellt!");
}

In diesem Beispiel besteht die Liste List aus einem Wert (i32) und einer Box, die auf den nächsten Listenknoten zeigt. Das Ende der Liste wird durch Nil markiert. Da die Liste rekursiv ist, wird die Größe zur Kompilierzeit nicht bestimmt, und daher wird Box<T> verwendet.


5. Dynamische Typen mit Box<dyn Trait>

In Rust können Traits (ähnlich wie Interfaces in anderen Sprachen) als dynamische Typen verwendet werden. Mit Box<dyn Trait> kann ein Wert, der eine bestimmte Trait-Implementierung hat, auf dem Heap gespeichert werden.

Beispiel: Trait-Objekte mit Box<dyn Trait>

trait Animal {
    fn speak(&self);
}

struct Dog;

impl Animal for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

fn main() {
    let my_dog: Box<dyn Animal> = Box::new(Dog);
    my_dog.speak(); // Dynamischer Dispatch
}

In diesem Beispiel implementiert der Typ Dog das Trait Animal. Mit Box<dyn Animal> kann der Wert Dog auf dem Heap gespeichert und dynamisch auf seine speak-Methode zugegriffen werden. Dies ermöglicht es, zur Laufzeit zwischen verschiedenen Implementierungen des Traits zu unterscheiden.


6. Speicherfreigabe mit Box

Eine der großen Stärken von Rust ist das Ownership-Modell. Wenn eine Box den Scope verlässt, wird der auf dem Heap gespeicherte Wert automatisch freigegeben, ohne dass man sich um die manuelle Speicherverwaltung kümmern muss.

Beispiel: Automatische Speicherfreigabe

fn main() {
    {
        let heap_value = Box::new(10);
        println!("Heap value: {}", heap_value);
    } // Hier wird `heap_value` automatisch freigegeben
    println!("heap_value ist jetzt aus dem Scope entfernt.");
}

Sobald heap_value den Scope verlässt, wird der Speicher auf dem Heap automatisch freigegeben.

Online- und Präsenzkurse zum Thema

Finden Sie interessante und zum Thema passende Kurse

5-Tages Seminar Einführung in Rust

Dieses 5-tägige Rust-Seminar bietet eine fundierte Einführung in die Grundlagen von Rust, darunter Speicherverwaltung (Ownership, Borrowing), der Standardbibliothek und Fehlerbehandlung. Die Teilnehmer lernen, wie man sauberen und sicheren Rust-Code schreibt, Module importiert und die umfangreichen Funktionen der Rust-Standardbibliothek nutzt.

5 days fulltime Online

Nächster Termin: 20. Januar 2025
Preis pro Person: 1900,00 EUR

Rabattaktion: 3 für den Preis von 2!

Kein Seminar dabei?

Schreiben Sie uns an, vielleicht finden wir eine Möglichkeit.

Nutzen Sie dazu unser Kontaktformular.