pattern-uri (destructurare) în Dart

Desfac o valoare (record, listă, map, obiect) în variabile separate, într-o singură declarație.

Sintaxă
final (a, b) = record;                 // record
final [prim, al2, ...rest] = lista;     // listă
final {'nume': nume} = map;            // map
final Punct(:x, :y) = punct;           // obiect (getter-ele x și y)
for (final (cheie, valoare) in ...) { }
if (valoare case [int a, int b]) { }   // potrivire cu verificare

Ce face pattern-uri (destructurare)

Un pattern descrie forma unei valori. Folosit într-o declarație (final (nume, varsta) = elev;), el destructurează valoarea: scoate părțile ei în variabile noi. Funcționează cu records, liste ([a, b, ...rest]), map-uri ({'cheie': v}) și obiecte (Punct(:x, :y), care citește getter-ele x și y).

Pattern-urile apar și în buclele for-in (for (final MapEntry(:key, :value) in map.entries)), în switch și în if-case. Acolo pot și verifica: if (date case {'varsta': int v}) intră în bloc doar dacă map-ul are cheia și valoarea este int.

Într-o declarație, pattern-ul trebuie să se potrivească sigur (altfel apare o excepție la rulare); când datele pot avea altă formă, folosește if-case sau switch. Cu _ ignori o parte, iar (a, b) = (b, a); schimbă două variabile între ele.

Exemple

Exemplul 1: Destructurarea unui record și interschimbare

Patru variabile dintr-o singură declarație, apoi schimbarea valorilor fără variabilă temporară.

(String, String, int) citesteElev() => ('Ioana', 'Popa', 16);

void main() {
  final (prenume, nume, varsta) = citesteElev();
  print('$prenume $nume, $varsta ani');

  final (_, numeFamilie, _) = citesteElev();
  print('Doar numele de familie: $numeFamilie');

  var a = 'stânga', b = 'dreapta';
  (a, b) = (b, a);
  print('a=$a, b=$b');
}

Rezultat

Ioana Popa, 16 ani
Doar numele de familie: Popa
a=dreapta, b=stânga

Exemplul 2: Liste, map-uri și obiecte

...rest prinde restul listei; pattern-ul de map citește cheile; MapEntry(:key, :value) desface fiecare intrare.

void main() {
  final clasament = ['Ana', 'Mihai', 'Elena', 'Radu'];
  final [castigator, locul2, ...ceilalti] = clasament;
  print('Câștigător: $castigator, locul 2: $locul2, alții: $ceilalti');

  final adresa = {'oras': 'Sibiu', 'strada': 'Bălcescu', 'numar': 12};
  final {'oras': oras, 'numar': numar} = adresa;
  print('$oras, nr. $numar');

  final stoc = {'mere': 40, 'pere': 15};
  for (final MapEntry(key: fruct, :value) in stoc.entries) {
    print('$fruct: $value kg');
  }
}

Rezultat

Câștigător: Ana, locul 2: Mihai, alții: [Elena, Radu]
Sibiu, nr. 12
mere: 40 kg
pere: 15 kg

Exemplul 3: Verificare cu if-case

Datele vin dintr-o sursă nesigură; blocul rulează doar când forma și tipurile se potrivesc.

void proceseaza(Object? date) {
  if (date case {'nume': String nume, 'note': [int prima, ...]}) {
    print('$nume are prima notă $prima');
  } else if (date case [String oras, int temperatura]) {
    print('În $oras sunt $temperatura °C');
  } else {
    print('Format necunoscut: $date');
  }
}

void main() {
  proceseaza({'nume': 'Andrei', 'note': [9, 10]});
  proceseaza(['Galați', 23]);
  proceseaza({'nume': 'Andrei'});
}

Rezultat

Andrei are prima notă 9
În Galați sunt 23 °C
Format necunoscut: {nume: Andrei}

Greșeli frecvente

  • Un pattern de listă cu altă lungime într-o declarație: final [a, b] = [1, 2, 3]; aruncă StateError la rulare. Adaugă ... sau folosește if-case.
  • Să crezi că pattern-ul de map cere toate cheile: {'oras': o} se potrivește și dacă map-ul are alte chei în plus.
  • Punct(:x) pentru un câmp privat sau inexistent: pattern-ul de obiect folosește getter-ele publice ale clasei.

Comenzi înrudite

  • records: Grupuri de valori anonime și imutabile, de forma (a, b) sau (nume: a), ideale pentru a întoarce mai multe valori.
  • switch: Alege un bloc de cod după valoarea sau forma unei expresii; în Dart 3 fiecare case este un pattern.
  • expresii switch: Forma de switch care produce o valoare: fiecare ramură este pattern => expresie.
  • when (guard): Adaugă o condiție suplimentară la un pattern din switch sau if-case; cazul se alege doar dacă e adevărată.
  • for-in: Parcurge pe rând elementele unei liste, unui set sau oricărui Iterable, fără indice.
  • sealed: Clasă cu un set închis de subtipuri, toate în același fișier; switch-ul pe ea verifică dacă ai tratat toate cazurile.