作者 / Michael Thomsen

Dart 2.12 现已发布,其中包含 健全的空安全Dart FFI 的稳定版。空安全是我们最新主打的一项生产力强化功能,意在帮助您规避空值错误,以前这种错误通常很难被发现,您可以观看下面这支视频了解详情。FFI 则是一种互操作机制,支持调用以 C 语言编写的既有代码,例如调用 Windows Win32 API。欢迎大家即刻开始使用 Dart 2.12。

Dart 平台的独特功能

在详细了解健全空安全和 FFI 之前,我们先来讨论一下它们在哪些方面契合了我们对 Dart 平台的期望。编程语言往往有很多类似的功能,例如,很多语言都支持面向对象的编程或在 web 上运行。真正将各个语言区分开来的,是其独特的功能组合。

Dart 具有横跨三个维度的独特功能组合:

  • 可移植性: 高效的编译器可针对设备生成 x86 和 ARM 机器代码,并针对 web 生成优化的 JavaScript。同时兼容移动设备、桌面 PC、应用后端等多种 目标平台。大量的开发库和 package 提供了可在所有平台上使用的一致的 API,进一步降低了开发者创建真正多平台应用的成本。

  • 高生产力: Dart 平台支持热重载,因此可在原生设备和 web 上实现快速迭代开发。此外,Dart 还提供了丰富的结构,如 isolates 和 async/await 等,用以处理和实现常见的并发和事件驱动的应用模式。

  • 稳健: Dart 的健全空安全类型系统可以在开发过程中就捕捉到错误。整个平台拥有极好的可扩展性和可靠性,已经被大量且多样的应用在累计超过十年的生产环境中实战检验过,其中包括 Google 的一些关键业务应用,如 Google Ads 和 Google Assistant 等。

健全空安全增强了类型系统的稳健性,同时提高了性能。借助 Dart FFI,您可以获得更强的可移植性,同时沿用由 C 语言编写的既有代码,在处理对性能要求极为严苛的任务时,可以尽情使用经过精心优化的 C 语言代码。

健全的空安全

Dart 2.0 中引入健全类型系统以来,Dart 语言中最重大的新增内容便是健全空安全。空安全进一步增强了类型系统,让您能够捕捉到空值错误,此类错误经常导致应用崩溃。启用空安全后,您就可以在开发过程中捕捉到空值错误,避免应用在生产环境中发生崩溃。

健全空安全的设计围绕一套核心原则展开。您可以阅读 官方文档 了解这些原则对开发者的影响。

默认不可空: 从根本改变类型系统

在空安全出现之前,开发者面临的核心挑战在于无法区分预期收到空值的代码和不接受空值的代码。几个月前,我们在 Flutter 的 master 渠道中发现了一个错误,多个 flutter 工具命令在特定计算机配置下会发生崩溃,并触发空值错误: 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) {
...

您发现错误了吗?由于 version 可能为空,所以 majorminor 也可能为空。如果单独检查此处代码,这一错误似乎并不难发现。但实际上,即使经过了严格的代码审查过程 (如 Flutter repo 所采用的代码审查流程),也总是难免有这样的漏网之鱼。在启用空安全后,静态分析能够立即捕捉到这一问题 (如下图)。您可以 在 DartPad 中亲自上手体验

△ IDE 中的分析结果

这只是一个非常简单的错误。我们早期在 Google 内部的代码中使用空安全时,捕捉到的复杂错误远多于此。其中一些是多年前就已经发现的 bug,但在通过空安全进行额外的静态检查前,很多团队都未能找到原因。

  • 内部团队发现,他们经常检查表达式中是否存在空值,而这些表达式永远不可能为空。这个问题在使用 protobuf 的代码中最常见,其中可选字段在未经设置时会返回一个默认值,而且永不为空。这会导致代码混淆默认值和空值,并错误地检查默认条件。
  • Google Pay 团队在他们的 Flutter 代码中发现了一些 bug,在尝试访问 Widget 上下文之外的 Flutter State 对象时会出错。在采用空安全之前,这些对象会返回 null 并掩盖错误;在采用空安全之后,健全分析确定这些属性永远不可能为空,并会给出分析错误。
  • Flutter 团队发现了一个 bug: 如果在 Window.render() 中向 scene 参数传递空值,则 Flutter 引擎可能会崩溃。在向空安全迁移的过程中,他们添加了一个提示,将 Scene 标记为不可空,即可轻松防止空值可能引发的应用崩溃。

在默认不可空的前提下工作

启用空安全 后,声明变量的基础方法会发生变化,因为默认类型不可为空:

// 在空安全的 Dart 中,以下均不可为空
var i = 42; // Inferred to be an int.
String name = getFileName();
final b = Foo();

如果您想要创建可能同时包含值或 null 的变量,则需要在声明变量时在类型后面显式添加 ? 后缀:

// aNullableInt 可以为整型或 null
int? aNullableInt = null;

空安全的实现很稳健,并提供丰富的静态流程分析,方便开发者轻松处理可空类型。例如,局部变量在进行空值检查后,Dart 会将其类型从可空提升为非空:

int definitelyInt(int? aNullableInt) {
if (aNullableInt == null) {
return 0;
}
// aNullableInt 现在会被提示为非空 int
return aNullableInt;
}

我们还添加了一个新的关键字,required。当一个命名的参数被标记为 required (在 Flutter widget API 中经常出现),而调用者忘记提供该参数时,就会发生如下分析错误:

渐进迁移至空安全

空安全对于我们的类型系统而言是一项根本性的改变,因此如果我们执意强制所有开发者采用,势必会造成严重的混乱。因此,我们想让您自行决定合适的迁移时机,空安全将是一项可选特性: 在做好准备之前,您可以在无需强制启用空安全的情况下使用 Dart 2.12。您甚至可以在尚未启用空安全的应用或 package 中依赖已启用空安全的 package。

为了帮助您将现有代码迁移至空安全,我们提供了迁移工具和 迁移指南。该工具会首先分析您所有的代码,然后您可以交互式地查看工具推断出的可空属性,如果您不同意工具得出的结论,则可以添加可空性提示以更改推断。添加迁移提示可能会大幅提升迁移质量。

目前,在默认情况下,使用 dart createflutter create 新创建的 package 和应用中不会启用健全空安全。在大部分生态系统完成迁移后,我们预计将在后续的稳定版本中默认启用。您可以通过 dart migrate 在新创建的 package 或应用中轻松 启用空安全

Dart 生态系统的空安全迁移状态

去年,我们提供了健全空安全的数个预览版和 Beta 版,旨在为生态系统提供首批支持空安全的 package。这项工作非常重要,我们建议大家 有序迁移至健全空安全,也就是说,在所有依赖项迁移完成之前,最好不要迁移自己的 package 或应用。

我们已发布由 DartFlutterFirebaseMaterial 团队所提供的数百个 package 的空安全版本。令人惊喜的是,Dart 和 Flutter 生态系统对此也予以巨大的支持,pub.dev 现在共有 1,000 多个 package 支持空安全。而且重要的是,最受欢迎的 package 已率先完成迁移,截止到 Dart 2.12 发布时,前 100 个最受欢迎的 package 中已有 98 个支持空安全,而在前 250 和前 500 的 package 中,支持空安全的比例则为 78% 和 57%。我们希望在接下来的几周,pub.dev 上能够出现更多支持空安全的 package。我们的分析 表明,pub.dev 上的绝大多数 package 已经可以 开始迁移

充分健全的空安全的优势

完成迁移后,您的项目就处于健全的空安全模式下了。这意味着 Dart 能够完全确保具有不可空类型的表达式不为空。当 Dart 分析完您的代码并确定某个变量不可为空时,该变量将始终不可为空。Dart 与 Swift 都拥有健全的空安全,但有些编程语言在这方面仍有待改进。

Dart 的健全空安全还暗含另一项备受期待的优势: 您的程序可以更小、更快。由于 Dart 能够确保不可为空的变量绝不为空,因此可以 实现优化。例如,Dart 的运行前 (ahead-of-time, AOT) 编译器可以生成更小更快的原生代码,因为当其知道变量不为空时,便不再需要添加空值检查了。

Dart FFI: 集成 Dart 与 C 语言代码库

您可以通过 Dart FFI 调用 C 语言编写的既有代码库,从而增强可移植性,还可以通过精心打磨的 C 代码完成对性能要求极为严苛的任务。从 Dart 2.12 起,Dart FFI 已结束 Beta 测试阶段,现已进入稳定状态,可以用于生产环境。我们还添加了一些新功能,包括嵌套结构和按值传递结构。

按值传递结构

在 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 中增加了新的便利函数,所以在常见的情况下,无需额外添加关于分配和释放内存的模板代码:

// 分配一个 Utf8 数组,使用 Dart 字符串填充,然后传递给 C 方法并转换结果,最后释放 arg
//
// API 变更前:
final pointer = allocate<Int8>(count: 10);
free(pointer);
final arg = Utf8.toUtf8('Michael');
var result = helloWorldInC(arg);
print(Utf8.fromUtf8(result);
free(arg);
// API 变更后:
final pointer = calloc<Int8>(10);
calloc.free(pointer);
final arg = 'Michael'.toNativeUtf8();
var result = helloWorldInC(arg);
print(result.toDartString);
calloc.free(arg);

自动生成 FFI 绑定

对于大型的 API 接口,编写与 C 代码集成的 Dart 绑定极其耗时。为减轻这一负担,我们为大家准备了绑定生成器,可以通过 C 头文件自动创建 FFI 封装代码,欢迎试用

FFI 路线图

核心 FFI 平台完成后,我们的工作重心将转向基于核心平台扩展 FFI 功能集。我们正在研究的一些功能包括:

  • ABI 特定数据类型,如 int、long、size_t (#36140)
  • 结构中的内联数组 (#35763)
  • Packed 结构 (#38158)
  • 联合类型 (#38491)
  • 对 Dart 开放终结方法 (finalizer) (#35770,请注意,您现在可以通过 C 语言使用终结方法)

FFI 使用示例

在过去的几个月中,我们看到大家在使用 Dart FFI 集成一系列基于 C 语言的 API 时,发掘出了许多有创意的用法。下面介绍几个示例:

Dart 语言的未来计划

健全空安全是这几年我们对 Dart 语言做出的最大改变。接下来,我们将继续稳步改进 Dart 语言和平台。下面简单介绍一些我们在 语言设计规划 中实验的内容:

  • 类型别名 (#65): 将创建类型别名的功能扩展到非函数类型。例如,您可以创建一个 typedef 并将其用作变量类型:
typedef IntList = List<int>;
IntList il = [1,2,3];
  • 无符号右移运算符 (#120): 添加新的 >>> 运算符,便于对整数执行无符号移位操作。此运算符可完全重写。

  • 通用元数据注解 (#1297): 扩展元数据注解,以同时支持含类型参数的注解。

  • 静态元编程 (#1482): 支持静态元编程,即编译期间生成新 Dart 源代码的 Dart 程序,与 Rust 宏 (macro) 和 Swift 函数构建器 (function builder) 类似。该功能目前仍处于早期探索阶段,但我们认为它可能会开启全新用例的大门,打破现在依赖代码生成的僵局。

Dart 2.12 现已发布

欢迎大家下载 Dart 2.12Flutter 2.0 SDK,即刻开始使用 Dart 2.12,尽情体验健全空安全和稳定版 FFI。请大家阅览 DartFlutter 的已知空安全问题。如果您发现其他任何问题,请在 Dart 问题跟踪页 中报告给我们。

如果您已在 pub.dev 上发布了 package,请立即参阅 迁移指南,了解如何迁移至健全空安全。迁移有助于依赖您的 package 的其他 package 和应用完成迁移。我们在此向已经完成迁移的开发者们表示感谢!

欢迎大家与我们分享自己的健全空安全和 FFI 体验,我们评论区见!

Dart 2.12 现已发布的更多相关文章

  1. Dart 2.16 现已发布

    文 / Michael Thomsen, Dart 产品经理 Dart 2.16 正式发布 Dart 2.16 正式版已于上周发布.尽管没有新的语言特性加入,但本次版本发布包含了数个问题修复 (包括对 ...

  2. Dart 2.15 现已发布

    作者 / Michael Thomsen, Dart & Flutter Product Manager, Google 我们已经正式发布了 Dart SDK 的 2.15 版本,该版本新增了 ...

  3. Dart 2.13 版现已发布

    作者 / Kevin Moore & Michael Thomsen Dart 2.13 版现已发布,其中新增了类型别名功能,这是目前用户呼声第二高的语言功能.Dart 2.13 还改进了 D ...

  4. Microsoft Message Analyzer (微软消息分析器,“网络抓包工具 - Network Monitor”的替代品)官方正式版现已发布

    Microsoft Message Analyzer (微软消息分析器,“网络抓包工具 - Network Monitor”的替代品)官方正式版现已发布 来自官方日志的喜悦 被誉为全新开始的消息分析器 ...

  5. Github Atom 1.12.0-beta3 发布

    Github Atom 1.12.0-beta3 发布了,Atom 是 Github 专门为程序员推出的一个跨平台文本编辑器.具有简洁和直观的图形用户界面,并有很多有趣的特点:支持CSS,HTML,J ...

  6. Windows优化大师最新版 V7.99 Build 12.604发布

    本文由 www.169it.com 收集整理 Windows优化大师是一款功能强大的系统工具软件,它提供了全面有效且简便安全的系统检测.系统优化.系统清理.系统维护四大功能模块及数个附加的工具软件.使 ...

  7. Fuel4D 2.1 免费跨平台游戏引擎 现已发布

    Fuel4D 2.1 免费跨平台游戏引擎 现已发布 开发环境:纯 C/C++,无JAVA.O-C. 支持 WIN32.安卓 系统. 详情见官方网站:http://www.fuel4d.com 或者进F ...

  8. Tapdata 的 2.0 版 ,开源的 Live Data Platform 现已发布

    https://www.bilibili.com/video/BV1tT411g7PA/?aid=470724972&cid=766317673&page=1 点击上方链接,一分钟快速 ...

  9. KubeEdge 1.12版本发布,稳定性、安全性、可扩展性均带来大幅提升

    摘要:2022年9月29日,KubeEdge发布1.12版本.新版本新增多个增强功能,在扩展性.稳定性.安全性上均有大幅提升. 本文分享自华为云社区<KubeEdge 1.12版本发布,稳定性. ...

  10. OGG for DB2 i 12.2发布

    2016-04-15 Oracle发布了GoldenGate for DB2 i 12.2.0.1.2,软件可以从OTN 或 eDelivery下载.这是第一个针对DB2 for i的12.2版本.此 ...

随机推荐

  1. AT_abc218_d 题解

    洛谷链接&Atcoder 本篇题解为此题较简单做法及较少码量,并且码风优良,请放心阅读. 题目简述 给定一个平面内的 \(N\) 个点的坐标,求这 \(N\) 个点中选 \(4\) 个点可构成 ...

  2. Python版RNA-seq分析教程:差异表达基因分析

    Bulk RNA-seq 分析的一个重要任务是分析差异表达基因,我们可以用 omicverse包来完成这个任务.对于差异表达分析而言,首先,我们可> 以先将 gene_id 改为 gene_na ...

  3. 倒装句&强调句

    倒装句 你[吃][胡萝卜]了吗? 吃胡萝卜了吗,[你]? 强调点不同 汉语常见于口语表达 英语则常见于书面用语 英语的语序是 主语 谓语(动词) 通常把谓语动词提前 1.完全倒装句 谓语部分完全放在主 ...

  4. 【SpringCloud】 Re01

    简单理解 接口跨服务调用 说白了 就是原来只是调用一个接口就能得到资源,现在是调用两个或者更多接口,这些接口来自不同的服务 (从前端的角度来看依然只是调用这个接口,只是这个接口背后又去调用其他的接口了 ...

  5. 【CentOS】rpm包安装Jdk

    1.系统环境检查 前提情要:[如果是使用虚拟机的Linux系统,强烈建议先打个快照备份一下,以免操作失误无法重来] 首先查看系统是否存在java环境 java -version 因为点选了环境工具,这 ...

  6. 【SpringMVC】11 拦截器

    拦截器是AOP具体的应用 只能使用SpringMVC自己的组件有效 之拦截访问控制器方法的请求, 如果访问的是jsp.html.css.img.js这一类的静态资源,则不会拦截 演示: 编写一个拦截器 ...

  7. 如果美国断供中国所有的Intel和AMD芯片,国内各行各业会不会崩溃

    说一个我个人观点,我认为如果国内完全没有X86芯片的供应,那么各行各业的发展会明显进入发展迟缓阶段,首先受影响的就是软件开发领域,因为没有新的芯片也就意味着袋电脑性能停滞或者倒退,那么开发出新的更耗资 ...

  8. 【英伟达】GTC 2024|黄仁勋2小时演讲精华版|六大亮点| Blackwell GPU | DGX B200 | NVL72 | cuLitho | GROOT | Omniverse Cloud —— 智能机器人元年

    视频地址: https://www.youtube.com/watch?v=zBIddyiMXsU

  9. 标准DQN在测试算法性能时为什么要将探索概率epsilon设置为0.05呢,而不是使用其他探索概率的epsilon-greedy策略或者直接使用greedy探索策略呢?

    标准dqn的策略网络参数更新所采用的规则为Q-learning中的更新规则,总所周知的是Q-learning是异策略算法,异策略算法就是行为策略和评估策略(更新所得策略)是不同的. 更新规则: q-l ...

  10. 构建无服务器数仓(三 )EMR Serverless 操作要点、优化以及开放集成测试

    引言 在数据驱动的世界中,企业正在寻求可靠且高性能的解决方案来管理其不断增长的数据需求.本系列博客从一个重视数据安全和合规性的 B2C 金融科技客户的角度来讨论云上云下混合部署的情况下如何利用亚马逊云 ...