Я — Евгений Сатуров, Head of Flutter в Surf и ведущий Flutter Dev Podcast. По традиции представляю перевод официальной статьи про новый релиз языка Dart 2.17, который идёт бок о бок с последним мажорным релизом мультиплатформенного фреймворка Flutter 3.0. Текст дополнен моими комментариями.
На Google I/O мы анонсировали новый Dart SDK версии 2.17. В основу релиза легли важные вопросы повышения продуктивности и портируемости на другие платформы. В этот релиз мы добавили новые языковые фичи: усовершенствовали enum с поддержкой enum-членов и передачу параметров в суперклассы, а также сняли некоторые ограничения для именованных параметров.
Мы улучшили тулинг: выпустили новую мажорную версию package:lints
— инструмента, который проверяет Dart-код на соответствие лучшим практикам — и сделали масштабное обновление документации к основным библиотекам (добавили примеры кода).
Для улучшения интеграции с платформами добавили новые шаблоны для dart:ffi
(взаимодействие с нативным C) во Flutter-плагинах, экспериментальную поддержку процессоров RISC-V, а также поддержку подписи исполняемых файлов для macOS и Windows.
Новые языковые фичи, повышающие продуктивность
В Dart 2.17:
Появилась полнофункциональная поддержка enum-членов.
Изменился подход к работе с именованными аргументами в конструкторах.
Код, с помощью которого параметры передаются в суперклассы, стал более лаконичным и простым.
Улучшенный enum и enum-члены
С помощью перечислений enum удобно представлять дискретные наборы состояний. К примеру, можно смоделировать воду как enum Water { frozen, lukewarm, boiling }
. Но что если нам надо добавить методов к enum
? Скажем, чтобы конвертировать каждое из состояний в температуру или представить enum
в виде String
.
Пожалуй, можно использовать extension-методы и добавить метод waterToTemp()
, но в таком случае нужно быть осторожным и синхронизировать его с enum
. В случае с конвертацией в String
мы бы предпочли переопределить toString()
, но такой возможности раньше не было.
В Dart 2.17 мы добавили поддержку enum-членов. Это значит, что вы можете добавлять:
поля, содержащие состояние;
конструкторы, задающие это состояние;
методы с реализацией или даже переопределять уже существующие методы.
Многие из вас просили добавить такую возможность: она заняла третье место в голосовании за желаемые фичи языка.
В примере с водой теперь можно добавить поле int
, содержащее температуру, и дефолтный конструктор, который принимает int
:
enum Water {
…
final int tempInFahrenheit;
const Water(this.tempInFahrenheit);
}
Чтобы убедиться, что конструктор вызывается при создании enum
, нужно вызывать его для каждого значения enum
:
enum Water {
frozen(32),
lukewarm(100),
boiling(212);
…
}
Чтобы обеспечить преобразование в String
, переопределяем toString
, который enum
наследуют от Object
:
@override
String toString() => "The $name water is $tempInFahrenheit F.";
Таким образом вы получите завершённый enum
, который легко инстанцировать и у которого можно вызвать метод:
void main() {
print(Water.frozen); // Prints “The frozen water is 32 F.”
}
Ниже — подробный пример использования двух этих подходов. Как нам кажется, код в новой версии Dart 2.17 куда проще читать и поддерживать.
Комментарий Евгения Сатурова
Авторы оригинальной статьи немного лукавят. В реальности мы просто не использовали enum из языка Dart. А если и использовали, то только в тех местах, где никакого другого выхода не было. Мы использовали enum-like самописные классы, но… конечно, это не то, чего ты ожидаешь от языка, который изо всех сил хочешь считать хорошим.
Новый enum единогласно одержал победу в неформальном голосовании в нашей команде на звание самого значимого изменения языка с момента появления поддержки null-safety.
Если разработчики Dart и дальше будут учитывать популярность issues в трекере при выборе новых фич для реализации, то совсем скоро мы увидим data-классы, множественный return, а, быть может, и отмену обязательной точки с запятой. Хотя, что это я… совсем размечтался.
Инициализаторы суперклассов
Если у вас имеется иерархия наследования классов, чаще всего параметры конструктора передают конструктору суперкласса. Для этого подкласс должен:
Перечислить каждый параметр в собственном конструкторе.
Вызвать суперконструктор с этими параметрами.
В результате рождается бойлерплейт: много повторений, код сложнее читать и ещё муторнее поддерживать.
Несколько членов сообщества Dart помогли это исправить: они предложили ввести новую конструкцию, которая бы означала, что тот или иной параметр уже указан в суперклассе.
Мы решили, что идея отличная, и добавили её в Dart 2.17. Решение особенно хорошо подходит для Flutter-кода с виджетами. Более того, когда мы применили эту фичу во Flutter, то обнаружили, что суммарно код сократился почти на две тысячи строк!
Пишем именованные аргументы где угодно
Мы переделали механизм работы именованных аргументов при вызове метода. Раньше такие сущности, согласно правилам, оказывались в конце списка аргументов соответствующего метода. Это раздражает, если хочется сделать код читабельнее и разместить в конце позиционный аргумент.
К примеру, ниже показано, как вызывается конструктор List<T>.generate
. Раньше аргумент growable пришлось бы расположить в конце: там его легко потерять после крупного позиционного аргумента, который ещё и сам содержит генератор. Теперь располагать их можно в произвольном порядке: небольшой именованный аргумент можно вывести в начало, а генератор — в конец.
Подробнее — в обновлённых примерах для:
Комментарий Евгения Сатурова
Две описанные выше доработки выглядят незначительно, но их важность раскрывается в полной мере в кодовой базе действительно больших и запутанных проектов. Мы с осторожностью относимся к синтаксическому сахару, особенно когда выгода от его использования не очевидна и делится на ноль неоднозначностью, которую он привносит в код.
К счастью, обе доработки – это лаконичность, которая всё ещё помогает коду «рассказывать историю».
Изменения в ключевые инструменты для повышения продуктивности
В Dart 2.14 мы включали package:lints
. Вместе с анализатором Dart он помогал избегать ошибок при написании Dart-кода и следовать каноническому стилю, который облегчал код-ревью.
С тех пор в анализаторе стало доступно несколько новых линтеров: мы тщательно их отсортировали и выбрали десять новых линтеров для любого кода на Dart и два новых линтера, касающихся кода во Flutter. Сюда вошли линтеры, с помощью которых можно убедиться, что импортируемые сущности включены в файл pubspec, и избежать ошибок с проверками на null в параметрах. Также появились линтеры, обеспечивающие консистентность форматирования дочерних свойств. Обновиться до новых линтеров можно с помощью команды:
Для пакетов Dart:
dart pub upgrade —-major-versions lints
Для пакетов Flutter:
flutter pub upgrade —-major-versions flutter_lints
SecureSockets часто используют для работы с TCP-сокетами, защищёнными TLS- и SSL-протоколами. До выхода Dart 2.17 дебажить такие сокеты в процессе разработки было проблематично: отсутствовала возможность проверить защищённый процесс передачи данных.
Однако теперь мы добавили возможность указывать файл keyLog
. В таком случае текстовая строка в формате NSS Key Log добавляется к файлу, как только программа и сервер обмениваются новыми TLS-ключами. Благодаря этому анализаторы сетевого трафика (к примеру, Wireshark) получают возможность дешифровать информацию, отправленную через сокет. Подробнее об этом можно почитать в документации по API для SecureSocket.connect().
Документация к API, сгенерированная инструментом dart doc
, — это крайне ценный ресурс для большинства Dart-разработчиков, изучающих новые API. Несмотря на то, что API наших базовых библиотек уже давно сопровождаются исчерпывающими текстовыми описаниями, многие разработчики поделились, что предпочитают изучать API по примерам кода.
В Dart 2.17 мы полностью переработали основные библиотеки и добавили примеры кода на 200 наиболее популярных страниц. Сравните документацию к dart:convert в Dart 2.16 и обновлённую страницу для Dart 2.17.
Продуктивность растёт не только с появлением новых фич на платформе, но и с уменьшением их числа — если из стека удаляются фичи, которыми давно не пользуются. Таким образом, масштаб новой информации остаётся сравнительно небольшим: это крайне важно для неопытных разработчиков.
Мы удалили 231 строк кода со статусом deprecated из библиотеки dart:io
. Если вы до сих пор пользуетесь этими deprecated API, вы можете обновиться с помощью dart fix и использовать API, пришедшие им на замену. Также мы продолжили устранять deprecated инструменты Dart CLI, и в этот раз удалили dartdoc
(вместо него можно использовать dart doc
) и pub
(можно использовать dart pub
или flutter pub
).
Расширение кроссплатформенной интеграции и поддержки
Вторая ключевая тема этого релиза — кроссплатформенная интеграция и поддержка. Dart — самый настоящий кроссплатформенный язык. Мы уже поддерживаем широкий спектр платформ и непрерывно двигаемся вперёд, чтобы обеспечить возможность глубокой интеграции с каждой из поддерживаемых платформ, а также поддержать молодые развивающиеся платформы.
Dart FFI, наш ключевой механизм взаимодействия с C/нативным кодом, — популярный способ интеграции Dart-кода в уже имеющийся нативный код платформы. Во Flutter это отличная возможность для создания плагинов, которые используют нативные API из платформы устройства (к примеру win32 API на Windows).
В Dart 2.17 и Flutter 3 мы добавили шаблоны к консольной команде flutter
. Теперь вы можете с лёгкостью создавать FFI плагины, в которых Dart API поддерживается обращениями dart:ffi
к нативному коду. Более подробную информацию можно найти на обновлённой странице «Разработка пакетов и плагинов» на flutter.dev.
Мы добавили в FFI поддержку платформ с ABI-специфичными типами. К примеру, теперь можно использовать Long (long
в языке C), чтобы точнее представить длинный целочисленный тип с ABI-специфичным размером, который может составлять 32 бит и 64 бит в зависимости от архитектуры ЦП. С полным списком поддерживаемых типов вы можете ознакомиться в перечне под названием «Implementers» на странице AbiSpecificInteger API.
При глубокой интеграции с нативными платформами через Dart FFI иногда требуется расставить приоритеты при очистке памяти или других ресурсов (портов, файлов и так далее), занятых Dart-кодом или нативным кодом. Эта задача всегда была непростой: Dart управляет сборкой мусора автоматически.
В Dart 2.17 она решается с помощью концепта под названием Finalizer, у которого есть интерфейс-маркер Finalizable
. С его помощью можно «помечать» объекты, которые не следует завершать или удалять преждевременно. Появился класс NativeFinalizer
, который можно прикрепить к объекту Dart и таким образом запустить колбэк, когда объекту будет грозить удаление. Вместе две этих сущности позволяют запускать сборку мусора как в нативном, так и в Dart-коде.
Подробную информацию с описанием и примерами вы можете прочитать в документации к API NativeFinalizer или в документации к WeakReferences и Finalizer, если вас интересует поддержка аналогичных возможностей в простом Dart-коде.
Поддержка компиляции Dart в нативный код — ключевой фактор, благодаря которому у приложений на Flutter отличная производительность при запуске и быстрый рендеринг. Второй фактор — возможность компилировать Dart в исполняемые файлы с помощью dart compile. Такие файлы можно запускать самостоятельно на любой машине без необходимости устанавливать Dart SDK. Ещё одна возможность, которую мы добавили в Dart 2.17, — поддержка подписи исполняемых файлов, благодаря которой можно развернуть файл на Windows или macOS, где зачастую требуется подпись.
Помимо этого мы продолжаем расширять набор поддерживаемых платформ и следим за разработками новых платформ. К примеру, RISC-V — новый инновационный набор инструкций для процессоров. RISC-V International, международная некоммерческая организация, владеет правами на RISC-V спецификации и предоставляет свободный доступ к инструкциям бесплатно. Пока платформа ещё совсем новая, но мы считаем, что у неё есть потенциал: в релиз 2.17.0–266.1.beta
для Linux (и далее в бета-канале) мы включили её экспериментальную поддержку.
Нам очень интересно ваше мнение! Обязательно поделитесь своими впечатлениями в разделе Issues или напишите отзыв здесь.
Dart 2.17 готов к работе
Чтобы начать, скачайте релиз Dart 2.17 отдельно или вместе с релизом Flutter 3 SDK.
И не пропустите новый контент, к которому мы открыли доступ в рамках Google I/O.
Комментарий Евгения Сатурова
Новый релиз Dart 2.17 удался. Все без исключения изменения важные и нужные. Спешите обновляться до Flutter 3, чтобы получить доступ к новым возможностям языка.
Ну а мы возобновляем набор в нашу Flutter-команду. Если вы хотите вместе с нами заниматься развитием опен-сорса, работать над сложными и интересными R&D задачами, а также делать красивые и функциональные приложения, будем рады познакомиться и поработать вместе!
lamerok
Есть ли в DART возможность динамически выполнять код самого DART, скажем аналог Eval в JavaSctipt?
danial72
Нет и не будет
Borz
https://pub.dev/packages/dart_eval ?