Как стать автором
Обновить
70.32
Surf
Создаём веб- и мобильные приложения

Dart 2.12: Sound null safety и Dart FFI отправлены на стабильный канал

Время на прочтение10 мин
Количество просмотров3.9K
Автор оригинала: Майкл Томсен

Surf представляет перевод статьи, анонсирующей выход Dart 2.12. Да, знаем, что сильно опоздали с публикацией: оригинал вышел 3 марта. Тем не менее, Dart 2.12 — важный релиз, и мы решили опубликовать материал.

Вышел Dart 2.12. В нём — стабильные версии sound null safety и Dart FFI.

Null safety – наша самая свежая масштабная фича. Её цель – повысить производительность: помочь вам избежать null error. Проблемы, вызванные этой ошибкой, зачастую тяжело заметить в коде. Об этом мы подробно рассказали во вводном видео.

FFI – это механизм взаимодействия, позволяющий вызывать уже имеющийся код на языке C. Например, обращаться к Windows Win32 API. Доступ к Dart 2.12 уже открыт.

Уникальные функциональные возможности платформы, на которой работает Dart

Прежде чем подробнее поговорить о sound null safety и FFI, предлагаю обсудить, как они связаны с нашими планами развития платформы под Dart. Как правило у языков программирования много похожих возможностей. К примеру, многие языки поддерживают объектно ориентированное программирование или работу в web. Настоящие различия между языками заключаются в их уникальных комбинациях функциональных возможностей.

Уникальная комбинация функциональных возможностей для создания приложений

Переносимость

Нативная производительность: Компиляция в машинный код или оптимизированный JS

Развитые, консистентные мультиплатформенные библиотеки

Продуктивность

Быстрый hot reload для поэтапной разработки

Многопоточность и изоляты

Надёжность

Надёжная null-safe система с динамической идентификацией типов

Масштабируемость и надёжность уровня Google

Уникальные функциональные возможности Dart можно распределить по трём направлениям:

  • Переносимость. Продуктивные компиляторы генерируют машинный код x86 и ARM для устройств и оптимизированный JavaScript код для web. Dart поддерживает целый ряд целевых платформ: мобильные приложения, ПК, серверную часть приложений и многое другое. Богатый выбор библиотек и пакетов обеспечивает консистентность API, работающих на всех платформах: создавать мультиплатформенные приложения становится ещё дешевле.

  • Продуктивность. Платформа для Dart поддерживает hot reload, благодаря чему процесс разработки становится быстрым и итеративным. Кроме того, в Dart есть такие многофункциональные структурные элементы, как изоляты и async/await для работы с распространенными многопоточными и событийно-ориентированными паттернами в приложениях.

  • Надёжность. Надёжная null-safe система типов в Dart находит ошибки прямо в процессе разработки. Платформа в целом очень надёжна, хорошо масштабируется. Более десяти лет она используется в продакшн множеством приложений, включая, к примеру, Google Ads и Google Assistant, бесперебойная работа которых имеет критическое значение для бизнеса.

Sound null safety делает систему типов ещё надежнее и помогает повысить производительность. Благодаря Dart FFI можно использовать уже существующие библиотеки C. Это улучшает переносимость и позволяет интегрировать хорошо отлаженный код на C в задачах, для которых критически важна производительность.

Sound null safety

Sound null safety – самое масштабное дополнение к языку Dart с тех пор, как в Dart 2.0 появилась надежная система типов. Null safety делает систему типов ещё надёжнее. Она позволяет обнаруживать ошибки типа null error ещё в процессе разработки, а значит, вы избежите крашей, когда приложение выйдет в продакшн. Sound null safety базируется на нескольких ключевых принципах.

Non-nullable по умолчанию: фундаментальное изменение системы типов

До появления null safety основная проблема была в том, что тяжело заметить разницу между кодом, который ожидает значение null, и кодом, который не обрабатывает null.

Несколько месяцев назад мы обнаружили баг в основном канале Flutter. При определённых конфигурациях машины различные команды инструментов Flutter крашились и выдавали null error: The method '>=' was called on null. Причиной стал вот такой код:

final int major = version?.major;

final int minor = version?.minor;

if (globals.platform.isMacOS) {

 // plugin path of Android Studio changed after version 4.1.

 if (major >= 4 && minor >= 1) {

 ...

final int major = version?.major; final int minor = version?.minor; if (globals.platform.isMacOS) {  // plugin path of Android Studio changed after version 4.1.  if (major >= 4 && minor >= 1) {  ...Видите ошибку? Так как у version может быть значение null, оно может быть и у major , и у minor. Когда этот баг вырван из контекста, кажется, что его легко заметить, но на практике такие ошибки всё время остаются незамеченными, даже если код проверяют очень тщательно, как в репозитории Flutter. С приходом null safety такие ошибки тут же всплывают в результатах статического анализа. Попробуйте, как это работает в DartPad.

Скриншот с результатом анализа в IDE
Скриншот с результатом анализа в IDE

И это довольно простая ошибка. Когда мы ещё только начинали использовать null safety в коде Google, нам встречались куда более запутанные ошибки. О некоторых багах было уже годами известно, но наши команды не могли найти их причину без помощи дополнительных статических проверок от null safety. Вот несколько примеров:

  • В одной команде обнаружили, что часто проверяют на значения null те выражения, в которых в принципе не может быть null. Чаще всего с этой проблемой сталкивались в коде, использующем protobuf, где необязательные поля возвращают значение по умолчанию, если оно не задано, и не могут вернуть null. В результате код неправильно проверял условия по умолчанию: путал значение по умолчанию и значение null.

  • Члены команды Google Pay нашли баги в своём коде во Flutter, когда не смогли получить доступ к объектам Flutter State вне контекста Widget. До null safety такие объекты возвращали null и маскировали ошибку; с null safety глубокий анализ показал, что null не может быть значением в этом случае, и диагностировал ошибку ещё до этапа компиляции.

  • Команда Flutter нашла баг, от которого движок Flutter может крашиться, если параметр scene в Window.render() получает null. Во время миграции на null safety они добавили хинт — mark Scene as non-nullable, благодаря которому легко смогли предотвратить возможные краши приложения, вызванные значением null.

Работа с non-nullable по умолчанию

После перехода на null safety основы объявления переменных меняются, потому что типы по умолчанию становятся non-nullable:

// In null-safe Dart, none of these can ever be null.

var i = 42; // Inferred to be an int.

String name = getFileName();

final b = Foo();

Если вы хотите создать переменную, которая может содержать значение или null, вам необходимо это обозначить в описании переменной, добавив суффикс ? к типу:

// aNullableInt can hold either an integer or null.

int? aNullableInt = null;

Null safety реализована очень надёжно: статический анализ с множеством функциональных возможностей облегчает работу с nullable типами. К примеру, после проверки null, Dart повышает тип локальной переменной с nullable до non-nullable:

int definitelyInt(int? aNullableInt) {

 if (aNullableInt == null) {

   return 0;

 }

 // aNullableInt has now promoted to a non-null int.

 return aNullableInt;

}

Ещё мы добавили новое ключевое слово, required. Если обратиться к именованному параметру, обозначенному как required (что часто происходит в API виджетов во Flutter) и забыть предоставить аргумент, возникнет ошибка:

Поэтапная миграция на null safety

Null safety – фундаментальное изменение нашей системы типов. Чтобы избежать множества проблем, мы не переводим на него принудительно: вам решать, когда лучше мигрировать. 

Null safety – добровольный выбор: вы можете пользоваться Dart 2.12 и не обязаны при этом переходить на null safety. Вы даже можете использовать пакеты, уже поддерживающие null safety, независимо от того, поддерживает ваше приложение или пакет null safety или нет.

Чтобы помочь вам с миграцией уже имеющегося кода на null safety, у нас есть инструмент для миграции и гайд по миграции

Сначала инструмент анализирует весь ваш код. Затем вы можете самостоятельно проверить допустимость значений null в обнаруженных случаях. Если вы не согласны с каким-то из результатов, можете добавить подсказку и таким образом изменить результат. Такие миграционные подсказки могут сильно улучшить качество миграции.

Пока что новые пакеты и приложения, собранные с dart create и flutter create, не поддерживают sound null safety. Мы планируем это исправить в будущем стабильном релизе, когда убедимся, что большая часть экосистемы мигрировала. Новый пакет или приложение можно легко перевести на null safety с помощью dart migrate.

Статус миграции на null safety в экосистеме Dart

За последний год мы делали несколько превью и бета-сборок sound null safety, чтобы наполнить экосистему пакетами, поддерживающими null safety. Подготовительный этап крайне важен, так как мы рекомендуем мигрировать на sound null safety в определенном порядке: пакет или приложение следует переводить на null safety только после того, как мигрируют все их зависимости.

Мы уже опубликовали null-safe-версии сотен пакетов от команд Dart, Flutter, Firebase и Material. Помимо этого нам невероятно помогло сообщество: более тысячи пакетов на pub.dev уже поддерживают null safety. 

Важно отметить, что самые популярные пакеты мигрировали первыми: 98% из 100 самых популярных пакетов, 78% из 250, и 57% из 500 уже перешли на поддержку null safety как раз к релизу. Мы надеемся, что в ближайшие недели на pub.dev появится ещё больше пакетов с null safety. Результаты нашего анализа показали, что подавляющее большинство пакетов на pub.dev уже разблокированы и могут начать процесс миграции.

Преимущества sound null safety

Как только вы завершите миграцию, Dart станет абсолютно непротиворечивым (sound). Dart на 100% гарантирует, что переменные non-nullable типа не могут быть null. Когда Dart анализирует код и определяет переменную как non-nullable, эта переменная всегда будет non-nullable. Кроме Dart, sound null safety есть в Swift, но в целом таких языков программирования не очень много.

Sound null safety в Dart имеет ещё один плюс: ваши программы смогут стать меньше и быстрее. Так как Dart уверен, что non-nullable переменные никогда не имеют значения null, он может оптимизировать код. Например, Статический (AOT) компилятор Dart может генерировать более компактный и быстрый нативный код, потому что ему не надо добавлять проверку на nulls: он уже знает, что значение переменной не null.

Dart FFI для интеграции Dart с библиотеками C

Dart FFI помогает эффективно использовать уже имеющийся в библиотеках C код, что улучшает переносимость и в то же время позволяет интегрировать хорошо отлаженный код на C в задачах, для которых критически важна производительность. На момент выхода Dart 2.12, Dart FFI перешел из бета-версии в стабильную и готов к применению в продакшн. Мы также добавили к нему несколько новых фич, включая вложенные структуры (struct) и возможность передавать структуры по значению.

Передача структур по значению

В коде на языке C структуры можно передавать как по ссылке, так и по значению. Раньше FFI поддерживал только передачу по ссылке, но начиная с Dart 2.12 можно передавать структуры и по значению. Приведу небольшой пример двух функций на C, в которых структура передаётся как по ссылке, так и по значению:

struct Link {

 double value;

 Link* next;

};

void MoveByReference(Link* link) {

 link->value = link->value + 10.0;

}

Coord MoveByValue(Link link) {

 link.value = link.value + 10.0;

 return link;

}

Вложенные структуры

В языке C API часто используют вложенные структуры. Они содержат внутри себя другие структуры. Пример:

struct Wheel {

 int spokes;

};

struct Bike {

 struct Wheel front;

 struct Wheel rear;

 int buildYear;

};

С выходом Dart 2.12 FFI поддерживает вложенные структуры.

Изменения в API

Для перехода к стабильному FFI, а также для поддержки уже упомянутых фич, мы внесли несколько небольших изменений в API.

Теперь создавать пустые структуры не разрешается (изменение без обратной совместимости #44622), и разработчик получает предупреждение о депрекации. Вместо пустых структур можно использовать новый тип, Opaque. В функциях dart:ffi sizeOf, elementAt и ref теперь требуется использовать аргументы, тип которых определяется во время компиляции (изменение без обратной совместимости #44621). В package:ffi добавлены новые вспомогательные функции, чтобы выделить и освободить память в распространённых случаях. Теперь больше не требуется дополнительного бойлерплейта:

// Allocate a pointer to an Utf8 array, fill it from a Dart string,

// pass it to a C function, convert the result, and free the arg.

//

// Before API change:

final pointer = allocate<Int8>(count: 10);

free(pointer);

final arg = Utf8.toUtf8('Michael');

var result = helloWorldInC(arg);

print(Utf8.fromUtf8(result);

free(arg);

// After API change:

final pointer = calloc<Int8>(10);

calloc.free(pointer);

final arg = 'Michael'.toNativeUtf8();

var result = helloWorldInC(arg);

print(result.toDartString);

calloc.free(arg);

Автоматическая генерация биндингов в FFI

В случае с большими API очень много времени может уйти на то, чтобы написать в Dart биндинги, интегрирующиеся с кодом на C. Чтобы сделать вашу жизнь проще, мы собрали генератор биндингов, который автоматически создает обёртки FFI из заголовочных файлов на C. Предлагаем вам попробовать, что получилось: package:ffigen.

План развития FFI

Теперь, когда основная часть FFI платформы готова, мы хотим добавить к набору фич FFI фичи, которые будут накладываться на основу платформы. Вот несколько интересующих нас фич:

Примеры использования FFI

Нам встречалось много интересных способов применения Dart FFI для интеграции с различными API на основе языка C. Вот несколько примеров:

  • open_file один API, с помощью которого можно открывать файлы на нескольких платформах. С помощью FFI он вызывает API на нативной ОС (Windows, macOS и Linux).

  • win32 оборачивается вокруг большинства распространённых API Win32, благодаря чему можно вызывать целый ряд API Windows непосредственно из Dart.

  • objectbox – «быстрая» база данных, поддерживаемая кодом на C.

  • tflite_flutter с помощью FFI даёт Dart доступ к TensorFlow Lite API.

Чего ждать от языка Dart в будущем?

Sound null safety – самое крупное из изменений, внесённых в язык Dart за последние несколько лет. Мы хотим постепенно вносить чуть менее масштабные изменения в язык и платформу, опираясь на уже имеющийся крепкий фундамент. По ссылке можете посмотреть, с чем мы сейчас экспериментируем в проекте по разработке языка.

Псевдонимы типов (type aliases) (#65): Возможность создавать псевдонимы других типов, помимо функций. Например, создавать typedef и использовать его как переменную:

typedef IntList = List<int>;

IntList il = [1,2,3];

Оператор тройного сдвига (triple-shift operator) (#120): новый, полностью переопределяемый оператор >>>, с помощью которого можно будет побитово сдвигать беззнаковые целые числа.

Мета-аннотации дженерики (Generic metadata annotations) (#1297): дополнение мета-аннотаций аннотациями, содержащими аргументы типа.

Статическое метапрограммирование (Static meta-programming) (#1482): поддержка статического метапрограммирования — программ на Dart, производящих новый исходных код Dart в процессе компиляции, аналогично макросам для Rust и function builder-ам в Swift. Эта фича пока на начальном этапе исследования. По нашему мнению, с её помощью можно будет воплотить пользовательские сценарии, которые сегодня полагаются на автоматическую кодогенерацию.

Dart 2.12 уже доступен

Dart 2.12 с поддержкой sound null safety и стабильным FFI уже можно использовать в SDK Dart 2.12 и Flutter 2.0. Но сначала просим вас ознакомиться с известными проблемами при миграции на null safety в Dart и во Flutter. Если вы столкнулись с не упомянутой нами проблемой, пожалуйста, сообщите о ней в Dart issue tracker.

Если вы разрабатывали пакеты, опубликованные на pub.dev, просим вас сегодня же ознакомиться с руководством по миграции и изучить, как мигрировать на sound null safety. Если вы переведете свой пакет на null safety, скорее всего, это поможет разблокировать другие зависящие от него пакеты и приложения.

Также хотим выразить огромную благодарность тем, кто уже мигрировал!

Нам очень интересно узнать о вашем опыте работы с sound null safety и FFI. Пожалуйста, оставьте комментарий или напишите нам в Твиттере: @dart_lang.

Больше полезного про Flutter — в телеграм-канале Surf Flutter Team. Публикуем кейсы, лучшие практики, новости и вакансии Surf, а также проводим прямые эфиры. Присоединяйтесь!

Теги:
Хабы:
+7
Комментарии0

Публикации

Информация

Сайт
surf.ru
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия