records în Dart

Grupuri de valori anonime și imutabile, de forma (a, b) sau (nume: a), ideale pentru a întoarce mai multe valori.

Sintaxă
var r = ('Ana', 17);                     // record pozițional, tip (String, int)
var r = (nume: 'Ana', varsta: 17);       // câmpuri numite, tip ({String nume, int varsta})
r.$1, r.$2                               // câmpuri poziționale
r.nume                                   // câmp numit
(String, int) functie() => ('Ana', 17);  // mai multe valori întoarse

Ce face records

Un record pune mai multe valori împreună fără să definești o clasă: ('Ana', 17). Câmpurile pot fi poziționale (citite cu $1, $2…) sau numite ((oras: 'Iași', judet: 'IS'), citite cu .oras). Records sunt imutabile: câmpurile nu se pot schimba.

Tipul unui record este scris la fel: (String, int) sau ({String oras, String judet}). Cel mai des se folosesc pentru funcții care întorc mai multe valori, iar rezultatul se desface imediat cu un pattern: final (min, max) = limite(note);.

Două records sunt egale (==) dacă au aceeași formă și aceleași valori, deci pot fi folosite ca chei în Map sau elemente în Set. Un record cu un singur câmp pozițional are nevoie de virgulă: ('Ana',).

Exemple

Exemplul 1: O funcție care întoarce două valori

limite întoarce un record (int, int), desfăcut apoi în două variabile.

(int, int) limite(List<int> note) {
  var minim = note.first, maxim = note.first;
  for (final n in note) {
    if (n < minim) minim = n;
    if (n > maxim) maxim = n;
  }
  return (minim, maxim);
}

void main() {
  final rezultat = limite([7, 10, 5, 9]);
  print(rezultat);
  print('Cea mai mică: ${rezultat.$1}, cea mai mare: ${rezultat.$2}');

  final (mic, mare) = limite([8, 6]);
  print('$mic - $mare');
}

Rezultat

(5, 10)
Cea mai mică: 5, cea mai mare: 10
6 - 8

Exemplul 2: Câmpuri numite

Cu nume, codul se citește mai ușor decât cu $1 și $2. La afișare, câmpurile numite apar în ordine alfabetică.

({String oras, int locuitori}) celMaiMare(Map<String, int> orase) {
  final e = orase.entries.reduce((a, b) => a.value >= b.value ? a : b);
  return (oras: e.key, locuitori: e.value);
}

void main() {
  final r = celMaiMare({'Cluj-Napoca': 286598, 'Iași': 271692, 'Timișoara': 250849});
  print(r);
  print('${r.oras} are ${r.locuitori} de locuitori.');
}

Rezultat

(locuitori: 286598, oras: Cluj-Napoca)
Cluj-Napoca are 286598 de locuitori.

Exemplul 3: Egalitate și chei în Map

Records cu aceleași valori sunt egale, așa că se pot folosi drept coordonate într-un Map.

void main() {
  print(('Ana', 17) == ('Ana', 17));
  print((x: 1, y: 2) == (y: 2, x: 1));

  final harta = <(int, int), String>{
    (0, 0): 'Start',
    (2, 3): 'Comoară',
  };
  print(harta[(2, 3)]);
  print(harta[(1, 1)] ?? 'nimic aici');

  final unSingurCamp = ('doar unul',);
  print(unSingurCamp.$1);
}

Rezultat

true
true
Comoară
nimic aici
doar unul

Greșeli frecvente

  • ('Ana') fără virgulă nu este un record, ci doar un String în paranteze. Scrie ('Ana',).
  • Să încerci r.$1 = 'Ion': records sunt imutabile; creează un record nou.
  • Records pentru date complexe, folosite în tot programul: o clasă cu nume și metode este mai clară. Records sunt potrivite pentru valori de scurtă durată.

Comenzi înrudite

  • pattern-uri (destructurare): Desfac o valoare (record, listă, map, obiect) în variabile separate, într-o singură declarație.
  • funcții: Blocuri de cod cu nume, care primesc parametri și returnează o valoare; în Dart sunt și obiecte.
  • expresii switch: Forma de switch care produce o valoare: fiecare ramură este pattern => expresie.
  • generics: Clase și funcții cu tipuri ca parametri (<T>), care funcționează la fel pentru orice tip, păstrând verificarea tipurilor.