[Apprendre Rust ensemble | Modèles de conception] Grammaire idiomatique - Trait par défaut, Collection Smart Pointer, Destructor


avant-propos

Rust n'est pas un langage de programmation orienté objet traditionnel, et toutes ses fonctionnalités le rendent unique. Par conséquent, l'apprentissage des modèles de conception spécifiques à Rust est nécessaire. Cette série d'articles est constituée des notes d'étude de l'auteur pour l'apprentissage des "modèles de conception de rouille" et de ses propres idées.

Cet article présente principalement la grammaire idiomatique dans le design pattern Rust

  • trait par défaut
  • pointeur intelligent de collecte
  • destructeur

默认特质:Dans le développement de Rust, la nouvelle méthode ne peut pas être exigée partout. Pour résoudre ce problème, le trait Default est implémenté. En outre, il peut également être utilisé avec d'autres conteneurs.

集合智能指针:Utilisez le trait Deref pour transformer les collections en pointeurs intelligents, fournissant des vues détenues et empruntées des données.

析构函数:Rust ne fournit pas de code qui sera exécuté quelle que soit la façon dont la fonction se termine. Cependant, le destructeur d'un objet peut être utilisé pour exécuter du code qui doit être exécuté avant de quitter.


1. Caractéristiques par défaut

Dans Rust, de nombreux types ont un constructeur, mais celui-ci est spécifique à un certain type. Rust n'impose pas que chaque type doit avoir une nouvelle méthode en tant que constructeur. Pour répondre à cette exigence, Rust fournit le trait Default, qui peut être utilisé avec d'autres conteneurs.

注意:Certains conteneurs implémentent déjà le trait Default en place.

Un conteneur à un seul élément comme Cow, Box ou Arc peut non seulement implémenter des valeurs par défaut pour les types par défaut contenus, mais aussi automatiquement #[derive(Default)] sa structure pour tous les champs, donc plus il y a de types qui implémentent des valeurs par défaut , Plus il est utile.

D'autre part, le constructeur peut accepter plusieurs paramètres alors que la méthode default() ne le peut pas. Il peut même y avoir plusieurs constructeurs avec des noms différents, mais une seule implémentation par défaut par type.

Ce qui suit est un exemple

use std::{
    
    path::PathBuf, time::Duration};

// 导出默认值
#[derive(Default, Debug, PartialEq)]
struct MyConfiguration {
    
    
    // 选项默认为“None”
    output: Option<PathBuf>,
    // 默认空向量
    search_path: Vec<PathBuf>,
    // 持续时间默认为0
    timeout: Duration,
    // 布尔值默认为Flase
    check: bool,
}

impl MyConfiguration {
    
    
    // 在这添加setter
}

fn main() {
    
    
    // 构建一个拥有默认值的新实例
    let mut conf = MyConfiguration::default();
    // 做一些处理操作
    conf.check = true;
    println!("conf = {:#?}", conf);
        
    // 部分初始化创建实例
    let conf1 = MyConfiguration {
    
    
        check: true,
        ..Default::default()
    };
    assert_eq!(conf, conf1);
}

2. Définir des pointeurs intelligents

Utilisez le trait Deref pour transformer les collections en pointeurs intelligents, fournissant des vues détenues et empruntées des données. Voici un exemple d'utilisation.

use std::ops::Deref;

struct Vec<T> {
    
    
    data: RawVec<T>,
    //..
}

impl<T> Deref for Vec<T> {
    
    
    type Target = [T];

    fn deref(&self) -> &[T] {
    
    
        //..
    }
}

Vect<T> est une collection qui possède T, &[T] est une collection qui emprunte T, l'implémentation du trait Deref pour Vec permettra un déréférencement implicite de &Vec<T> à &[T]>

Tout comme String et &str, vous pouvez vous y référer.

La propriété et l'emprunt sont des aspects clés du langage Rust. Pour offrir une bonne expérience utilisateur, les structures de données doivent tenir compte de cette sémantique. Lors de la mise en œuvre d'une structure de données qui possède ses données, fournir une vue empruntée de ces données permet une API plus flexible.

La plupart des méthodes ne peuvent être implémentées que pour la vue empruntée, puis elles sont implicitement disponibles pour la vue propriétaire. Laissez les clients choisir entre emprunter ou s'approprier les données.

注意:Les méthodes et les traits disponibles uniquement par déréférencement ne sont pas pris en compte pour la vérification des limites, de sorte que la programmation générique des structures de données utilisant ce modèle peut devenir compliquée (voir les traits Borrow et AsRef, etc.).

3. Destructeur

Rust ne fournit pas de code qui sera exécuté quelle que soit la façon dont la fonction se termine. Cependant, le destructeur d'un objet peut être utilisé pour exécuter du code qui doit être exécuté avant de quitter.

Voici un exemple d'utilisation

fn bar() -> Result<(), ()> {
    
    
    // 这些不是必须要在函数中定义的,也可以在其他地方定义
    struct Foo;

    // 为Foo实现析构函数
    impl Drop for Foo {
    
    
        fn drop(&mut self) {
    
    
            println!("exit");
        }
    }

    // _exit的dtor将在退出函数“bar”时运行。
    let _exit = Foo;
    // 用?操作符隐式的返回
    baz()?;
    // 正常返回
    Ok(())
}

Si une fonction a plusieurs points de retour, il devient difficile et répétitif (et donc sujet aux bogues) d'exécuter du code à la sortie. Cela est particulièrement vrai si le retour est implicite en raison d'une macro. Quelle est la situation commune ? opérateur, retourne si le résultat est Err, mais continue si le résultat est Ok ? est utilisé comme mécanisme de gestion des exceptions, mais contrairement à Java (qu'il a finalement implémenté), il n'y a aucun moyen de planifier l'exécution du code dans des conditions normales et exceptionnelles. La panique quittera également la fonction plus tôt.

Le code dans le destructeur sera (presque) toujours exécuté pour gérer les paniques, les retours anticipés, etc.


Résumer

Ce qui précède est tout le contenu de cet article, présentant les trois parties de la grammaire idiomatique dans le modèle de conception Rust

  • trait par défaut
  • pointeur intelligent de collecte
  • destructeur

Grâce au contenu de ce numéro, j'espère que vous pourrez mieux comprendre le développement de Rust.

おすすめ

転載: blog.csdn.net/weixin_47754149/article/details/126594054