Recherche…


Syntaxe

  • struct Foo {field1: Type1, field2: Type2}
  • let foo = Foo {field1: Type1 :: new (), field2: Type2 :: new ()};
  • struct Bar (Type1, Type2); // type de tuple
  • let _ = Bar (Type1 :: new (), Type2 :: new ());
  • struct Baz; // type d'unité
  • let _ = Baz;
  • let Foo {field1, ..} = foo; // extrait le champ1 par correspondance de modèle
  • let Foo {field1: x, ..} = foo; // extraire field1 comme x
  • laisser foo2 = Foo {field1: Type1 :: new (), .. foo}; // construit à partir de l'existant
  • implémenter Foo {fn fiddle (& self) {}} // déclarer la méthode d'instance pour Foo
  • impliquer Foo {fn tweak (& mut self) {}} // déclarer la méthode d'instance mutable pour Foo
  • impliquer Foo {fn double (self) {}} // déclare la méthode d'instance propriétaire pour Foo
  • impliquer Foo {fn new () {}} // déclare la méthode associée pour Foo

Définir des structures

Les structures de Rust sont définies à l'aide du mot struct clé struct . La forme de structure la plus courante consiste en un ensemble de champs nommés:

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

Le ci-dessus déclare une struct avec trois champs: my_bool , my_num et my_string , respectivement des types bool , isize et String .

Une autre façon de créer des struct dans Rust consiste à créer une structure de tuple :

struct Bar (bool, isize, String);

Ceci définit un nouveau type, Bar , qui comporte trois champs sans nom, de type bool , isize et String , dans cet ordre. Ceci est connu comme le modèle newtype , car il introduit effectivement un nouveau "nom" pour un type particulier. Cependant, il le fait d'une manière plus puissante que les alias créés à l'aide du mot-clé type ; Bar est ici un type entièrement fonctionnel, ce qui signifie que vous pouvez écrire vos propres méthodes (ci-dessous).

Enfin, déclarez une struct sans champs, appelée structure unitaire :

struct Baz;

Cela peut être utile pour se moquer ou tester (lorsque vous souhaitez implémenter un trait de manière triviale) ou comme type de marqueur. En général, cependant, il est peu probable que vous rencontriez de nombreuses structures semblables à des unités.

Notez que les champs de struct dans Rust sont tous privés par défaut - c'est-à-dire qu'ils ne sont pas accessibles depuis le code en dehors du module qui définit le type. Vous pouvez préfixer un champ avec le mot-clé pub pour rendre ce champ accessible au public. De plus, le type de struct lui - même est privé. Pour que le type soit disponible pour les autres modules, la définition de la struct doit également être précédée de pub :

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

Créer et utiliser des valeurs de structure

Considérons les définitions de struct suivantes:

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

La construction de nouvelles valeurs de structure pour ces types est simple:

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;

Champs d'accès d'une structure utilisant . :

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

Une liaison mutable à une structure peut avoir ses champs mutés:

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

Les fonctionnalités de corrélation de Rust peuvent également être utilisées pour visualiser une 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);

Ou créez une structure en utilisant une deuxième structure en tant que "modèle" avec la syntaxe de mise à jour de Rust:

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

Méthodes de structure

Pour déclarer des méthodes sur une structure (c'est-à-dire des fonctions pouvant être appelées "sur" la struct , ou des valeurs de ce type de struct ), créez un bloc 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 indique ici une référence immuable à une instance de struct Foo est nécessaire pour invoquer la méthode fiddle . Si nous voulions modifier l'instance (comme changer l'un de ses champs), nous prendrions plutôt un &mut self (c'est-à-dire une référence mutable):

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

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

Enfin, nous pourrions également utiliser self (notez l'absence d'un & ) comme récepteur. Cela nécessite que l'instance appartienne à l'appelant et que l'instance soit déplacée lors de l'appel de la méthode. Cela peut être utile si vous souhaitez consommer, détruire ou transformer complètement une instance existante. Un exemple d'un tel cas d'utilisation est de fournir des méthodes de "chaînage":

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

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

Notez que nous avons également préfixés self avec mut afin que nous puissions muter soi avant de le retourner à nouveau. Le type de retour de la méthode double mérite également une explication. Self à Self intérieur d'un bloc d' impl fait référence au type auquel l' impl s'applique (dans ce cas, Foo ). Ici, c'est surtout un raccourci utile pour éviter de retaper la signature du type, mais dans les traits, il peut être utilisé pour faire référence au type sous-jacent qui implémente un trait particulier.

Déclarer une méthode associée (communément appelée "méthode de classe" dans d’autres langages) pour une struct tout simplement self argument de self . De telles méthodes sont appelées sur le type de struct lui-même, et non sur une instance de celui-ci:

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);

Notez que les méthodes de structure ne peuvent être définies que pour les types déclarés dans le module actuel. De plus, comme pour les champs, toutes les méthodes de structure sont privées par défaut et ne peuvent donc être appelées que par code dans le même module. Vous pouvez préfixer les définitions avec le mot-clé pub pour les rendre appelables ailleurs.

Structures génériques

Les structures peuvent être génériques sur un ou plusieurs paramètres de type. Ces types sont indiqués entre <> en référence au type:

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};

Plusieurs types peuvent être donnés en utilisant des virgules:

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

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

Les paramètres de type font partie du type, donc deux variables du même type de base, mais avec des paramètres différents, ne sont pas interchangeables:

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

Si vous voulez écrire une fonction qui accepte une struct indépendamment de son affectation de paramètre de type, cette fonction devra également être générique:

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

Mais si nous voulions écrire une fonction qui pourrait toujours imprimer gx ? Il faudrait restreindre T à un type pouvant être affiché. Nous pouvons le faire avec des limites de type:

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

La fonction hello est désormais définie uniquement pour les instances Gen dont le type T implémente fmt::Display . Si on essayait de passer un Gen<(bool, isize)> par exemple, le compilateur se plaindrait que hello n'est pas défini pour ce type.

Nous pouvons également utiliser des limites de type directement sur les paramètres de type de la struct pour indiquer que vous ne pouvez construire cette struct que pour certains types:

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

Toute fonction ayant accès à un GenB sait maintenant que le type de x implémente Hash , et donc qu’elle peut appeler .x.hash() . Des limites de type multiples pour le même paramètre peuvent être données en les séparant par un + .

Comme pour les fonctions, les limites de type peuvent être placées après le <> utilisant le mot-clé where :

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

Cela a la même signification sémantique, mais peut rendre la signature plus facile à lire et à formater lorsque vous avez des limites complexes.

Les paramètres de type sont également disponibles pour les méthodes d'instance et les méthodes associées de la 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}
    }
}

Si vous avez des limites de type sur le T Gen , celles-ci devraient également être reflétées dans les limites de type de l' impl . Vous pouvez également rendre les limites d' impl plus strictes pour indiquer qu'une méthode donnée n'existe que si le type satisfait une propriété particulière:

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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow