πŸ“¦

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

1

Positional record β€” var result = ('success', 200); access via $1, $2

2

Named record β€” var user = (name: 'Hong', age: 30); β†’ user.name, user.age for readability

3

Destructuring β€” var (:name, :age) = user; extracts multiple variables in one line

4

switch pattern matching β€” case String s when s.isNotEmpty: type+condition check, case _: wildcard

5

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

Use Cases

Returning success/failure with data: (bool success, String message) Safely parsing API response JSON with pattern matching