Irbisa · Шпаргалка
Паттерны и записи в Dart 3
Записи, деструктуризация, switch-выражения, if-case, sealed-типы, исчерпывающий разбор
Что такое запись (record) в Dart 3 и чем отличаются позиционные и именованные поля?
// The type is the shape: nothing declares (int, String) anywhere.(int, String) parse(String raw) => (raw.length, raw.toUpperCase()); typedef User = ({String name, int age}); // a typedef only names a shape void main() {…Как деструктурировать запись в переменные и где ещё можно использовать деструктурирующий паттерн?
typedef User = ({String name, int age}); (int, String) fetch() => (200, 'ok'); void main() { final (code, body) = fetch(); // positional: bound by position…Чем отличаются
switch-оператор иswitch-выражение в Dart 3?enum Status { active, blocked, deleted } // Expression: produces a value; arms are `pattern => value`.String label(Status s) => switch (s) { Status.active => 'Active', Status.blocked || Status.deleted => 'Unavailable',…Что выведет этот код и почему последний поиск промахивается, хотя ключ выглядит таким же?
void main() { print((1, 'a') == (1, 'a')); print((x: 1, y: 2) == (y: 2, x: 1)); print((1, 2) == (x: 1, y: 2)); final cache = <(int, String), String>{(1, 'en'): 'Hello'}; print(cache[(1, 'en')]); final byTags = <(List<String>, int), String>{(['a'], 1): 'x'}; print(byTags[(['a'], 1)]);}Когда стоит вернуть запись вместо класса или
Map, а когда запись оказывается неправильным выбором?// wrong: every caller re-guesses the keys and castsMap<String, dynamic> loadOld() => {'items': <String>[], 'total': 0}; // right: the shape is part of the signature(List<String> items, int total) loadPage() => (['a', 'b'], 42);…Что такое сопоставление с образцом (pattern matching) в Dart 3 и какие старые идиомы оно заменяет?
// before: is-checks, casts and nested conditionsString describeOld(Object json) { if (json is Map<String, dynamic>) { final user = json['user']; if (user is Map<String, dynamic> && user['name'] is String) { return 'user ${user['name'] as String}';…Что такое
sealed-класс, как он делаетswitchисчерпывающим и чем он лучше абстрактного класса для моделирования состояния?sealed class AuthState {} final class SignedOut extends AuthState {} final class SigningIn extends AuthState {}…Что делает
if (value case pattern when condition)и что эта конструкция заменяет?class Session { String? token;} // before: two reads of a mutable field and a `!`void sendOld(Session s) {…Что проверяет объектный паттерн вроде
Cart(total: > 100)и какой код он выполняет?class Item { const Item(this.price); final int price;} class Cart {…Как сопоставляется списочный паттерн вроде
[first, ..., last]и во что обходится именованный rest-элемент...final rest?String summary(List<String> names) => switch (names) { [] => 'nobody', [final only] => only, [final a, final b] => '$a and $b', [final first, ..., final last] => '$first … $last', // nothing copied };…Как map-паттерн сопоставляется с декодированным JSON и что происходит с лишними или отсутствующими ключами?
String greet(Map<String, Object?> json) { if (json case {'type': 'user', 'name': final String name, 'age': final int age}) { return '$name, $age'; // extra keys in json are ignored } if (json case {'type': 'user', 'name': final String name}) {…Что такое паттерны сравнения и логические паттерны и когда вместо них нужен guard
when?const adult = 18;const retirement = 65; String bracket(int age) => switch (age) { < 0 => 'invalid', < adult => 'minor',…Что выведет эта программа и почему важен порядок веток
switch?String describe(Object o) => switch (o) { int n when n > 10 => 'big int', int() => 'int', num n => 'num $n', String(isEmpty: true) => 'empty string', String s => 'string $s', _ => 'other', }; void main() { for (final v in [42, 7, 2.5, '', 'hi', true]) { print(describe(v)); }}Почему этот
switchне компилируется, хотя в нём упомянут каждый подтипResult?sealed class Result {} final class Ok extends Result { Ok(this.value); final int value;} final class Err extends Result { Err(this.message); final String message;} String show(Result r) => switch (r) { Ok(:final value) when value > 0 => 'positive $value', Err(:final message) => 'error: $message', };Найдите баг: надпись о загрузке так и не появляется, а пока данные грузятся, пользователи видят «Something went wrong».
sealed class LoadState {}final class Loading extends LoadState {}final class Loaded extends LoadState { Loaded(this.items); final List<String> items;}final class Failed extends LoadState {} String label(LoadState state) => switch (state) { Loading => 'Loading…', Loaded(:final items) => '${items.length} items', _ => 'Something went wrong', };Найдите баг: часть товаров из API отбрасывается как невалидная, хотя их JSON выглядит правильным.
class Product { Product(this.name, this.price); final String name; final double price;} // `json` comes straight from `jsonDecode(response.body)`Product? parseProduct(Map<String, Object?> json) { if (json case {'name': String name, 'price': double price}) { return Product(name, price); } return null; // reported upstream as "invalid product"}Какие возможности языка Dart 3.x, кроме записей и паттернов, вы использовали и что каждая из них заменила?
// Needs `sdk: ^3.13.0` in pubspec.yaml.enum Side { start, center, end } class Point(final int x, final int y); // 3.13: constructor and fields in the header extension type UserId(String value) {} // 3.3: a zero-cost typed wrapper…Что произойдёт, если добавить подтип в публичный
sealed-класс, и как безопасно выкатить такое изменение?Во что обходятся записи и паттерны в рантайме: что выделяет память и какой код выполняется при сопоставлении?