dart-use-pattern-matching

Par flutter · skills

Utilisez les expressions switch et le pattern matching lorsque c'est approprié

npx skills add https://github.com/flutter/skills --skill dart-use-pattern-matching

Implémentation des Patterns Dart

Sommaire

Stratégie de sélection de patterns

Applique des types de patterns spécifiques en fonction de la structure des données et du résultat souhaité. Suis ces directives conditionnelles :

  • Si tu valides et extrais depuis des données désérialisées (ex. JSON) : Utilise les patterns Map et List pour vérifier simultanément la structure et destructurer les paires clé-valeur.
  • Si tu gères plusieurs valeurs de retour : Utilise les patterns Record pour destructurer les champs directement en variables locales.
  • Si tu exécutes un comportement spécifique au type (Algebraic Data Types) : Utilise les patterns Object combinés avec les classes sealed pour assurer l'exhaustivité.
  • Si tu fais correspondre des plages numériques ou des conditions : Utilise les patterns Relationnels (>=, <=) et Logical-and (&&).
  • Si plusieurs cas partagent la même logique : Utilise les patterns Logical-or (||) pour partager un seul corps de cas ou une clause guard.
  • Si tu ignores certaines valeurs : Utilise le pattern Wildcard (_) ou un élément Rest sans correspondance (...) dans les collections.

Switch statements vs. expressions

Sélectionne la construction switch appropriée en fonction du contexte d'exécution :

  • Si tu produis une valeur : Utilise une switch expression.
    • Syntaxe : switch (value) { pattern => expression, }
    • Règle : Chaque cas doit être une seule expression. Pas de fallthrough implicite. Doit être exhaustive.
  • Si tu exécutes des statements ou des effets de bord : Utilise un switch statement.
    • Syntaxe : switch (value) { case pattern: statements; }
    • Règle : Les cas vides passent au cas suivant. Les cas non-vides font un break implicite (aucun mot-clé break requis).

Implémentations de patterns fondamentaux

Implémente les patterns en utilisant la syntaxe et les règles suivantes :

  • Logical-or (||) : pattern1 || pattern2. Les deux branches doivent définir exactement le même ensemble de variables.
  • Logical-and (&&) : pattern1 && pattern2. Les branches ne doivent pas définir de variables chevauchantes.
  • Relationnel : ==, !=, <, >, <=, >= suivis d'une expression constante.
  • Cast (as) : pattern as Type. Lève une exception si la valeur ne correspond pas au type. À utiliser pour affirmer fortement les types lors de la destructuration.
  • Null-check (?) : pattern?. Échoue la correspondance si la valeur est null. Lie la variable au type de base non-nullable.
  • Null-assert (!) : pattern!. Lève une exception si la valeur est null.
  • Variable : var name ou Type name. Lie la valeur correspondante à une nouvelle variable locale.
  • Wildcard (_) : Correspond à n'importe quelle valeur et l'ignore.
  • List : [pattern1, pattern2]. Correspond à des listes de longueur exacte à moins qu'un élément Rest (... ou ...var rest) soit utilisé.
  • Map : {"key": pattern}. Correspond à des maps contenant les clés spécifiées. Ignore les clés non correspondantes.
  • Record : (pattern1, named: pattern2). Correspond à des records de la forme exacte. Utilise :var name pour inférer le nom du getter.
  • Object : ClassName(field: pattern). Correspond à des instances de ClassName. Utilise :var field pour inférer le nom du getter.

Workflows

Progression des tâches : Implémentation du Pattern Matching

Copie cette checklist pour suivre la progression lors de l'implémentation d'une logique de pattern matching complexe :

  • [ ] Identifie la structure de données évaluée (JSON, Record, Class, Enum).
  • [ ] Sélectionne la construction switch appropriée (Expression pour les valeurs, Statement pour les effets de bord).
  • [ ] Définis les patterns requis (Object, Map, List, Record).
  • [ ] Extrais les données requises en utilisant les patterns Variable (var x, :var y).
  • [ ] Applique des clauses guard (when condition) pour la logique qui ne peut pas être exprimée via les patterns.
  • [ ] Gère les cas non correspondants en utilisant un Wildcard (_) ou une clause default (si tu n'utilises pas une classe sealed).
  • [ ] Lance le validateur d'exhaustivité.

Boucle de feedback : Vérification de l'exhaustivité

Lorsque tu fais une correspondance sur des classes sealed ou des enums, tu dois assurer que tous les sous-types sont gérés.

  1. Lance le validateur : Exécute dart analyze.
  2. Revois les erreurs : Cherche les erreurs "The type 'X' is not exhaustively matched by the switch cases".
  3. Corrige : Ajoute les patterns Object manquants pour les sous-types non gérés, ou ajoute un cas Wildcard (_) si un fallback par défaut est acceptable.

Exemples

Validation et Destructuring JSON

Utilise les patterns Map et List pour valider la structure et extraire les données en une seule étape.

Input :

var data = {
  'user': ['Lily', 13],
};

Implémentation :

if (data case {'user': [String name, int age]}) {
  print('User $name is $age years old.');
} else {
  print('Invalid JSON structure.');
}

Algebraic Data Types (Sealed Classes)

Utilise les patterns Object avec les switch expressions pour gérer exhaustivement les types familliaux.

Implémentation :

sealed class Shape {}

class Square implements Shape {
  final double length;
  Square(this.length);
}

class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

// La switch expression garantit l'exhaustivité grâce au modificateur `sealed`.
double calculateArea(Shape shape) => switch (shape) {
  Square(length: var l) => l * l,
  Circle(:var radius)   => math.pi * radius * radius,
};

Échange et Destructuring de variables

Utilise les patterns d'assignation de variables pour échanger des valeurs ou extraire des champs record sans variables temporaires.

Implémentation :

var (a, b) = ('left', 'right');
(b, a) = (a, b); // Swap values

// Destructuring d'une fonction de retour
var (name, age) = getUserInfo();

Clauses Guard et Logical-or

Utilise when pour évaluer des conditions arbitraires après qu'un pattern correspond.

Implémentation :

switch (shape) {
  case Square(size: var s) || Circle(size: var s) when s > 0:
    print('Valid symmetric shape with size $s');
  case Square() || Circle():
    print('Invalid or empty shape');
  default:
    print('Unknown shape');
}

Skills similaires