Dart Records & Pattern Matching
Dart 3.0 β multiple returns, destructuring, switch patterns
Records, introduced in Dart 3.0, are immutable types that group multiple values with parentheses without defining classes. Access via positional ($1, $2) or named fields (.name, .age), with destructuring to extract multiple variables in one line. Combined with pattern matching, switch/if-case can check types and conditions simultaneously, greatly simplifying code.
Recordsλ?
Dart 3.0μμ λμ λ Recordsλ ν΄λμ€ μ μ μμ΄ μ¬λ¬ κ°μ νλμ κ°μ²΄λ‘ κ·Έλ£Ήννλ λΆλ³(immutable) 컬λ μ νμ μ λλ€. ꡬ쑰μ νμ΄ν(structural typing)μ μ¬μ©νλ©°, κ°μ νλ ꡬ쑰λ₯Ό κ°μ§λ©΄ κ°μ νμ μΌλ‘ μ·¨κΈλ©λλ€.
1. μμΉ κΈ°λ° λ μ½λ (Positional Records)
κ΄νΈ μμ κ°μ λμ΄νλ©΄ μμΉ κΈ°λ° λ μ½λκ° λ©λλ€. $1, $2λ‘ μ κ·Όν©λλ€.
var person = ('νκΈΈλ', 30);
print(person); // (νκΈΈλ, 30)
print(person.$1); // νκΈΈλ
print(person.$2); // 30
2. λͺ λͺ λ λ μ½λ (Named Records)
νλμ μ΄λ¦μ λΆμ¬νλ©΄ κ°λ μ±μ΄ ν¬κ² ν₯μλ©λλ€. μμΉ κΈ°λ°κ³Ό νΌν© μ¬μ©λ κ°λ₯ν©λλ€.
// λͺ
λͺ
λ νλ
var person = (name: 'νκΈΈλ', age: 30);
print(person.name); // νκΈΈλ
print(person.age); // 30
// μμΉ + λͺ
λͺ
νΌν©
var data = ('νκΈΈλ', age: 30, active: true);
print(data.$1); // νκΈΈλ
print(data.age); // 30
print(data.active); // true
3. νμ μ΄λ Έν μ΄μ
λ μ½λ λ³μμ λͺ μμ νμ μ μ§μ ν μ μμ΅λλ€.
// μμΉ κΈ°λ° νμ
(String, int) person = ('νκΈΈλ', 30);
// λͺ
λͺ
λ νλ νμ
({String name, int age}) person = (name: 'νκΈΈλ', age: 30);
// νΌν© νμ
(String, {int age, bool active}) data = ('νκΈΈλ', age: 30, active: true);
4. ν¨μμμ λ€μ€ κ° λ°ν
Recordsμ κ°μ₯ μ€μ©μ μΈ μ¬μ© μ¬λ‘μ λλ€. λ³λ ν΄λμ€ μμ΄ ν¨μμμ μ¬λ¬ κ°μ λ°νν μ μμ΅λλ€.
(String, int) getUserInfo() {
return ('νκΈΈλ', 30);
}
void main() {
var (name, age) = getUserInfo();
print('μ΄λ¦: $name, λμ΄: $age');
// μ΄λ¦: νκΈΈλ, λμ΄: 30
}
5. ꡬ쑰 λΆν΄ (Destructuring)
λ μ½λμ κ° νλλ₯Ό κ°λ³ λ³μλ‘ μΆμΆν©λλ€. λͺ λͺ λ λ μ½λλ μΆμ½ ꡬ문λ μ§μν©λλ€.
var person = (name: 'νκΈΈλ', age: 30);
// μ 체 ꡬ쑰 λΆν΄ (λ³μλͺ
λ³κ²½)
var (name: userName, age: userAge) = person;
print('μ΄λ¦: $userName, λμ΄: $userAge');
// μΆμ½ ꡬ쑰 λΆν΄ (νλλͺ
κ·Έλλ‘)
var (:name, :age) = person;
print('μ΄λ¦: $name, λμ΄: $age');
6. λλ±μ± λΉκ΅ (Equality)
Recordsλ κ° κΈ°λ° λλ±μ±μ μ§μν©λλ€. κ°μ ꡬ쑰μ κ°μ κ°μ§λ©΄ λμΌν κ²μΌλ‘ νλ¨ν©λλ€.
var person1 = (name: 'νκΈΈλ', age: 30);
var person2 = (name: 'νκΈΈλ', age: 30);
var person3 = (name: 'κΉμ² μ', age: 25);
print(person1 == person2); // true
print(person1 == person3); // false
var p1 = ('νκΈΈλ', 30);
var p2 = ('νκΈΈλ', 30);
print(p1 == p2); // true
7. switch ν¨ν΄ λ§€μΉ
Dart 3.0μ ν¨ν΄ λ§€μΉμ switchλ¬Έμμ νμ
κ²μ¬μ 쑰건μ λμμ μνν©λλ€. when κ°λλ‘ μΆκ° 쑰건λ κ±Έ μ μμ΅λλ€.
void describe(Object obj) {
switch (obj) {
case (String name, int age):
print('μ΄λ¦: $name, λμ΄: $age');
default:
print('κΈ°ν κ°μ²΄: $obj');
}
}
// when κ°λ μ¬μ©
void process(dynamic value) {
switch (value) {
case (String n, int a) when a >= 18:
print('μ±μΈ: $n, $aμ΄');
case (String n, int a):
print('λ―Έμ±λ
μ: $n, $aμ΄');
default:
print('κΈ°ν κ°: $value');
}
}
process(('νκΈΈλ', 30)); // μ±μΈ: νκΈΈλ, 30μ΄
process(('κΉμν¬', 15)); // λ―Έμ±λ
μ: κΉμν¬, 15μ΄
8. if-case ν¨ν΄ λ§€μΉ
ifλ¬Έ μμμ ν¨ν΄ λ§€μΉμ μνν©λλ€. JSON νμ±μ΄λ νμ κ²μ¦μ νΉν μ μ©ν©λλ€.
void processValue(Object value) {
if (value case (String name, int age)) {
print('μ΄λ¦: $name, λμ΄: $age');
} else if (value case String s when s.length > 5) {
print('κΈ΄ λ¬Έμμ΄: $s');
} else {
print('μ²λ¦¬ν μ μλ κ°: $value');
}
}
processValue(('νκΈΈλ', 30));
// μ΄λ¦: νκΈΈλ, λμ΄: 30
9. μ€μ²© ν¨ν΄ λ§€μΉ
리μ€νΈμ λ μ½λλ₯Ό μ‘°ν©ν 볡μ‘ν ꡬ쑰λ ν¨ν΄μΌλ‘ λΆν΄ν μ μμ΅λλ€.
var data = [('νκΈΈλ', 30), ('κΉμ² μ', 25)];
if (data case [(String s, int i), var rest]) {
print('첫 λ²μ§Έ μ¬λ: $s, $iμ΄');
// 첫 λ²μ§Έ μ¬λ: νκΈΈλ, 30μ΄
print('λλ¨Έμ§: $rest');
// λλ¨Έμ§: (κΉμ² μ, 25)
}
10. μ€μ μμ : ν΅κ³ κ³μ°
Recordsλ‘ μ΅μκ°, μ΅λκ°, νκ· μ ν λ²μ λ°ννλ ν¨μμ λλ€.
(double min, double max, double average) calculateStats(
List<double> values) {
if (values.isEmpty) return (0, 0, 0);
double sum = 0;
double min = values[0];
double max = values[0];
for (var value in values) {
sum += value;
if (value < min) min = value;
if (value > max) max = value;
}
return (min, max, sum / values.length);
}
void main() {
var numbers = [10.5, 25.3, 17.2, 8.7, 30.1];
var (min, max, avg) = calculateStats(numbers);
print('μ΅μκ°: $min'); // μ΅μκ°: 8.7
print('μ΅λκ°: $max'); // μ΅λκ°: 30.1
print('νκ· κ°: $avg'); // νκ· κ°: 18.36
}
11. μ€μ μμ : API μλ΅ μ²λ¦¬
Recordsμ ν¨ν΄ λ§€μΉμ κ²°ν©νλ©΄ API μλ΅μ μμ νκ² μ²λ¦¬ν μ μμ΅λλ€.
(bool success, {String? data, String? error})
fetchUserData(String userId) {
if (userId == 'user123') {
return (true,
data: '{"name": "νκΈΈλ"}',
error: null);
} else {
return (false,
data: null,
error: 'μ¬μ©μλ₯Ό μ°Ύμ μ μμ΅λλ€.');
}
}
void main() {
var result = fetchUserData('user123');
if (result.$1) {
print('λ°μ΄ν°: ${result.data}');
}
var fail = fetchUserData('unknown');
if (!fail.$1) {
print('μ€λ₯: ${fail.error}');
}
}
ν΅μ¬ μ 리
- Records = ν΄λμ€ μλ λΆλ³ λ€μ€ κ° μ»¨ν μ΄λ (Dart 3.0+)
- μμΉ κΈ°λ°($1, $2) vs λͺ λͺ κΈ°λ°(.name, .age) μ ν κ°λ₯
- ꡬ쑰 λΆν΄λ‘ ν μ€μ μ¬λ¬ λ³μ μΆμΆ
- κ° κΈ°λ° λλ±μ± β κ°μ ꡬ쑰+κ°μ΄λ©΄ == true
- ν¨ν΄ λ§€μΉ(switch/if-case)κ³Ό κ²°ν©νλ©΄ νμ +쑰건 λμ κ²μ¬ κ°λ₯
Implementation Steps
Positional record β var result = ('success', 200); access via $1, $2
Named record β var user = (name: 'Hong', age: 30); β user.name, user.age for readability
Destructuring β var (:name, :age) = user; extracts multiple variables in one line
switch pattern matching β case String s when s.isNotEmpty: type+condition check, case _: wildcard
if-case β if (json case {'name': String name}) for JSON parsing and type validation in one step
Pros
- ✓ Lightweight grouping and returning of multiple values without classes
- ✓ Replace if-else chains with clean switch using pattern matching
Cons
- ✗ Only available in Dart 3.0+
- ✗ Complex nested patterns may actually hurt readability