Das Entdecken von assoziierten Typen in Rust

RustRustBeginner
Jetzt üben

This tutorial is from open-source community. Access the source code

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Lab untersuchen wir das Konzept assoziierter Typen in Rust, das es ermöglicht, die Lesbarkeit des Codes zu verbessern, indem innere Typen lokal innerhalb eines Traits als Ausgabetypen definiert werden. Dies wird durch Verwendung des type-Schlüsselworts innerhalb der Traitdefinition erreicht. Mit assoziierten Typen müssen Funktionen, die das Trait verwenden, die Typen A und B nicht mehr explizit ausdrücken, was den Code kompakter und flexibler macht. Wir schreiben ein Beispiel aus einem vorherigen Abschnitt um, um die Verwendung von assoziierten Typen in der Praxis zu veranschaulichen.

Hinweis: Wenn das Lab keinen Dateinamen angibt, können Sie einen beliebigen Dateinamen verwenden. Beispielsweise können Sie main.rs verwenden und es mit rustc main.rs &&./main kompilieren und ausführen.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust(("Rust")) -.-> rust/DataStructuresandEnumsGroup(["Data Structures and Enums"]) rust(("Rust")) -.-> rust/AdvancedTopicsGroup(["Advanced Topics"]) rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/DataTypesGroup -.-> rust/integer_types("Integer Types") rust/DataTypesGroup -.-> rust/type_casting("Type Conversion and Casting") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("Method Syntax") rust/AdvancedTopicsGroup -.-> rust/traits("Traits") rust/AdvancedTopicsGroup -.-> rust/operator_overloading("Traits for Operator Overloading") subgraph Lab Skills rust/variable_declarations -.-> lab-99354{{"Das Entdecken von assoziierten Typen in Rust"}} rust/integer_types -.-> lab-99354{{"Das Entdecken von assoziierten Typen in Rust"}} rust/type_casting -.-> lab-99354{{"Das Entdecken von assoziierten Typen in Rust"}} rust/function_syntax -.-> lab-99354{{"Das Entdecken von assoziierten Typen in Rust"}} rust/expressions_statements -.-> lab-99354{{"Das Entdecken von assoziierten Typen in Rust"}} rust/method_syntax -.-> lab-99354{{"Das Entdecken von assoziierten Typen in Rust"}} rust/traits -.-> lab-99354{{"Das Entdecken von assoziierten Typen in Rust"}} rust/operator_overloading -.-> lab-99354{{"Das Entdecken von assoziierten Typen in Rust"}} end

Assoziierte Typen

Das Verwenden von "Assoziierten Typen" verbessert die Gesamtlesbarkeit des Codes, indem innere Typen lokal in ein Trait als Ausgabetypen verschoben werden. Die Syntax für die trait-Definition lautet wie folgt:

// `A` und `B` werden im Trait über das `type`-Schlüsselwort definiert.
// (Hinweis: `type` hat in diesem Kontext eine andere Bedeutung als `type`,
// wenn es für Aliase verwendet wird).
trait Contains {
    type A;
    type B;

    // Aktualisierte Syntax, um generisch auf diese neuen Typen zu verweisen.
    fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
}

Beachten Sie, dass Funktionen, die das trait Contains verwenden, überhaupt nicht mehr A oder B ausdrücken müssen:

// Ohne Verwendung assoziierter Typen
fn difference<A, B, C>(container: &C) -> i32 where
    C: Contains<A, B> {... }

// Mit Verwendung assoziierter Typen
fn difference<C: Contains>(container: &C) -> i32 {... }

Schreiben wir das Beispiel aus dem vorherigen Abschnitt mit assoziierten Typen um:

struct Container(i32, i32);

// Ein Trait, das überprüft, ob 2 Elemente im Container gespeichert sind.
// Ruft auch den ersten oder letzten Wert ab.
trait Contains {
    // Definieren Sie hier generische Typen, auf die Methoden zugreifen können.
    type A;
    type B;

    fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
    fn first(&self) -> i32;
    fn last(&self) -> i32;
}

impl Contains for Container {
    // Geben Sie an, welche Typen `A` und `B` sind. Wenn der `input`-Typ
    // `Container(i32, i32)` ist, werden die `output`-Typen als `i32` und `i32`
    // bestimmt.
    type A = i32;
    type B = i32;

    // `&Self::A` und `&Self::B` sind hier ebenfalls gültig.
    fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
        (&self.0 == number_1) && (&self.1 == number_2)
    }
    // Greifen Sie auf die erste Zahl zu.
    fn first(&self) -> i32 { self.0 }

    // Greifen Sie auf die letzte Zahl zu.
    fn last(&self) -> i32 { self.1 }
}

fn difference<C: Contains>(container: &C) -> i32 {
    container.last() - container.first()
}

fn main() {
    let number_1 = 3;
    let number_2 = 10;

    let container = Container(number_1, number_2);

    println!("Enthält der Container {} und {}: {}",
        &number_1, &number_2,
        container.contains(&number_1, &number_2));
    println!("Erste Zahl: {}", container.first());
    println!("Letzte Zahl: {}", container.last());

    println!("Der Unterschied ist: {}", difference(&container));
}

Zusammenfassung

Herzlichen Glückwunsch! Sie haben das Lab zu Assoziierten Typen abgeschlossen. Sie können in LabEx weitere Labs absolvieren, um Ihre Fähigkeiten zu verbessern.