Suche…


Syntax

  • struct Foo {Feld1: Typ1, Feld2: Typ2}
  • let foo = foo {field1: Type1 :: new (), field2: Type2 :: new ()};
  • struct Bar (Type1, Type2); // Tupeltyp
  • let _ = Bar (Type1 :: new (), Type2 :: new ());
  • struct Baz; // einheitlicher Typ
  • sei _ = Baz;
  • lass Foo {Feld1, ..} = Foo; // extrahiere Feld1 durch Mustervergleich
  • sei Foo {Feld1: x, ..} = Foo; // extrahiere Feld1 als x
  • let foo2 = foo {field1: Type1 :: new (), .. foo}; // aus vorhandenen erstellen
  • impl Foo {fn Fiddle (& self) {}} // Instanzmethode für Foo deklarieren
  • impl Foo {fn tweak (& mut self) {}} // veränderbare Instanzmethode für Foo deklarieren
  • impl Foo {fn double (self) {}} // Die eigene Instanzmethode für Foo deklarieren
  • impl Foo {fn new () {}} // Zugeordnete Methode für Foo deklarieren

Strukturen definieren

Strukturen in Rust werden mit dem Schlüsselwort struct definiert. Die gebräuchlichste Strukturform besteht aus einer Reihe benannter Felder:

struct Foo {
    my_bool: bool,
    my_num: isize,
    my_string: String,
}

Oben wird eine struct mit drei Feldern my_bool : my_bool , my_num und my_string der Typen bool , isize und String .

Eine weitere Möglichkeit zum Erstellen von struct in Rust besteht im Erstellen einer Tupelstruktur :

struct Bar (bool, isize, String);

Dies definiert eine neue Art, Bar , die drei unbenannte Felder hat, vom Typ bool , isize und String , in dieser Reihenfolge. Dies wird als das neue Typmuster bezeichnet , da es effektiv einen neuen "Namen" für einen bestimmten Typ einführt. Dies ist jedoch leistungsfähiger als die mit dem Schlüsselwort type erstellten Aliasnamen. Bar ist hier ein voll funktionsfähiger Typ, dh Sie können Ihre eigenen Methoden dafür schreiben (unten).

Schließlich deklarieren Sie eine struct ohne Felder, die als einheitliche Struktur bezeichnet wird :

struct Baz;

Dies kann nützlich sein, wenn Sie sich verspotten oder testen (wenn Sie ein Merkmal trivial implementieren möchten) oder als Markentyp. Im Allgemeinen ist es jedoch unwahrscheinlich, dass Sie auf viele einheitliche Strukturen stoßen.

Beachten Sie, dass die struct in Rust standardmäßig alle privat sind. Sie können also nicht über Code außerhalb des Moduls angesprochen werden, das den Typ definiert. Sie können einem Feld das Schlüsselwort pub voranstellen, um dieses Feld öffentlich zugänglich zu machen. Darüber hinaus ist die struct ist der Typ selbst privat. Um den Typ für andere Module verfügbar zu machen, muss der struct auch pub vorangestellt werden:

pub struct X {
    my_field: bool,
    pub our_field: bool,
}

Strukturwerte erstellen und verwenden

Beachten Sie die folgenden struct :

struct Foo {
    my_bool: bool,
    my_num: isize,
    my_string: String,
}
struct Bar (bool, isize, String);
struct Baz;

Das Erstellen neuer Strukturwerte für diese Typen ist unkompliziert:

let foo = Foo { my_bool: true, my_num: 42, my_string: String::from("hello") };
let bar = Bar(true, 42, String::from("hello"));
let baz = Baz;

Auf Felder einer Struktur zugreifen mit . :

assert_eq!(foo.my_bool, true);
assert_eq!(bar.0, true); // tuple structs act like tuples

Bei einer veränderlichen Bindung an eine Struktur können Felder mutiert sein:

let mut foo = foo;
foo.my_bool = false;
let mut bar = bar;
bar.0 = false;

Die Pattern-Matching-Funktionen von Rust können auch verwendet werden, um einen Blick in eine struct :

// creates bindings mb, mn, ms with values of corresponding fields in foo
let Foo { my_bool: mb, my_num: mn, my_string: ms } = foo;
assert_eq!(mn, 42);
// .. allows you to skip fields you do not care about
let Foo { my_num: mn, .. } = foo;
assert_eq!(mn, 42);
// leave out `: variable` to bind a variable by its field name
let Foo { my_num, .. } = foo;
assert_eq!(my_num, 42);

Oder erstellen Sie eine Struktur mit einer zweiten Struktur als "Vorlage" mit der Aktualisierungssyntax von Rust:

let foo2 = Foo { my_string: String::from("world"), .. foo };
assert_eq!(foo2.my_num, 42);

Strukturmethoden

Erstellen Sie einen impl Block, um Methoden für eine Struktur zu deklarieren (dh Funktionen, die auf der struct "on" oder Werte dieses struct impl :

impl Foo {
    fn fiddle(&self) {
        // "self" refers to the value this method is being called on
        println!("fiddling {}", self.my_string);
    }
}

// ...
foo.fiddle(); // prints "fiddling hello"

&self gibt an, dass ein unveränderlicher Verweis auf eine Instanz von struct Foo erforderlich ist, um die fiddle Methode aufzurufen. Wenn wir die Instanz ändern möchten (z. B. eines ihrer Felder ändern), verwenden wir stattdessen ein &mut self (dh eine veränderbare Referenz):

impl Foo {
    fn tweak(&mut self, n: isize) {
        self.my_num = n;
    }
}

// ...
foo.tweak(43);
assert_eq!(foo.my_num, 43);

Schließlich könnten wir auch self als Empfänger verwenden (beachten Sie das Fehlen eines & ). Dies erfordert, dass die Instanz dem Aufrufer gehört und bewirkt, dass die Instanz beim Aufruf der Methode verschoben wird. Dies kann nützlich sein, wenn wir eine vorhandene Instanz verbrauchen, zerstören oder auf andere Weise vollständig transformieren möchten. Ein Beispiel für einen solchen Anwendungsfall ist die Bereitstellung von "Verkettungsmethoden":

impl Foo {
    fn double(mut self) -> Self {
        self.my_num *= 2;
        self
    }
}

// ...
foo.my_num = 1;
assert_eq!(foo.double().double().my_num, 4);

Beachten Sie, dass wir voran auch self mit mut , damit wir selbst mutieren können , bevor es wieder zurück. Der Rückgabetyp der double bedarf auch einer Erklärung. Self innerhalb eines impl Blocks bezieht sich auf den Typ, für den das impl gilt (in diesem Fall Foo ). Hierbei handelt es sich meistens um eine nützliche Abkürzung, um zu vermeiden, dass die Signatur des Typs erneut eingegeben wird. In Merkmalen kann sie jedoch verwendet werden, um auf den zugrunde liegenden Typ zu verweisen, der ein bestimmtes Merkmal implementiert.

Um eine assoziierte Methode (in anderen Sprachen allgemein als "Klassenmethode" bezeichnet) für eine struct zu deklarieren, lassen Sie das self Argument einfach weg. Solche Methoden werden für den struct selbst aufgerufen, nicht für eine Instanz davon:

impl Foo {
    fn new(b: bool, n: isize, s: String) -> Foo {
        Foo { my_bool: b, my_num: n, my_string: s }
    }
}

// ...
// :: is used to access associated members of the type
let x = Foo::new(false, 0, String::from("nil"));
assert_eq!(x.my_num, 0);

Beachten Sie, dass Strukturmethoden nur für Typen definiert werden können, die im aktuellen Modul deklariert wurden. Wie bei Feldern sind auch alle Strukturmethoden standardmäßig privat und können daher nur per Code in demselben Modul aufgerufen werden. Sie können Definitionen mit dem Schlüsselwort pub voranstellen, um sie an anderer Stelle aufrufbar zu machen.

Generische Strukturen

Strukturen können über einen oder mehrere Typparameter generisch gemacht werden. Diese Typen sind in <> wenn Sie sich auf den Typ beziehen:

struct Gen<T> {
    x: T,
    z: isize,
}

// ...
let _: Gen<bool> = Gen{x: true, z: 1};
let _: Gen<isize> = Gen{x: 42, z: 2};
let _: Gen<String> = Gen{x: String::from("hello"), z: 3};

Mehrere Typen können mit Komma angegeben werden:

struct Gen2<T, U> {
    x: T,
    y: U,
}

// ...
let _: Gen2<bool, isize> = Gen2{x: true, y: 42};

Die Typparameter sind Teil des Typs. Zwei Variablen desselben Basistyps, aber mit unterschiedlichen Parametern, sind nicht austauschbar:

let mut a: Gen<bool> = Gen{x: true, z: 1};
let b: Gen<isize> = Gen{x: 42, z: 2};
a = b; // this will not work, types are not the same
a.x = 42; // this will not work, the type of .x in a is bool

Wenn Sie eine Funktion schreiben möchten, die eine struct unabhängig von ihrer Typparameterzuordnung akzeptiert, muss diese Funktion auch als generisch definiert werden:

fn hello<T>(g: Gen<T>) {
    println!("{}", g.z); // valid, since g.z is always an isize
}

Aber was wäre, wenn wir eine Funktion schreiben wollten, die gx immer drucken konnte? Wir müssen T darauf beschränken, dass es von einem Typ ist, der angezeigt werden kann. Wir können dies mit Typgrenzen machen:

use std::fmt;
fn hello<T: fmt::Display>(g: Gen<T>) {
    println!("{} {}", g.x, g.z);
}

Die hello Funktion ist jetzt nur für Gen Instanzen definiert, deren T Typ fmt::Display implementiert. Wenn wir beispielsweise versucht haben, Gen<(bool, isize)> zu übergeben, wird der Compiler sich darüber beschweren, dass hello für diesen Typ nicht definiert ist.

Wir können Typgrenzen auch direkt in den Typparametern der struct um anzuzeigen, dass Sie diese struct für bestimmte Typen struct können:

use std::hash::Hash;
struct GenB<T: Hash> {
    x: T,
}

Jede Funktion, die Zugriff auf ein GenB weiß jetzt, dass der Typ von x Hash implementiert und somit .x.hash() kann. Sie können mehrere Typgrenzen für denselben Parameter angeben, indem Sie sie mit einem + trennen.

Wie bei Funktionen können die Typgrenzen mit dem Schlüsselwort where nach dem <> werden:

struct GenB<T> where T: Hash {
    x: T,
}

Dies hat dieselbe semantische Bedeutung, kann jedoch die Signatur bei komplexen Schranken leichter lesbar und formatierbar machen.

Typparameter stehen auch für Instanzmethoden und zugehörige Methoden der struct :

// note the <T> parameter for the impl as well
// this is necessary to say that all the following methods only
// exist within the context of those type parameter assignments
impl<T> Gen<T> {
    fn inner(self) -> T {
        self.x
    }
    fn new(x: T) -> Gen<T> {
        Gen{x: x}
    }
}

Wenn Sie Typ Grenzen auf haben Gen ‚s T , sollten diese auch in der Art Grenzen des widerspiegeln impl . Sie können auch die impl Schranken impl zu sagen, dass eine bestimmte Methode nur existiert, wenn der Typ eine bestimmte Eigenschaft erfüllt:

impl<T: Hash + fmt::Display> Gen<T> {
    fn show(&self) {
        println!("{}", self.x);
    }
}

// ...
Gen{x: 42}.show(); // works fine
let a = Gen{x: (42, true)}; // ok, because (isize, bool): Hash
a.show(); // error: (isize, bool) does not implement fmt::Display


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow