在我们详细研究可靠的空安全和FFI之前,让我们讨论一下Dart平台如何将它们适合我们的目标。编程语言倾向于共享许多的功能。例如,许多语言都支持面向对象的编程或者可以在Web上运行。真正使语言与众不同的是它们独特的功能组合
Dart的独特功能涵盖了三个方面:
x86
和ARM
机器代码,并为Web生成优化的JavaScript。一个广泛的目标是支持:移动设备,台式机,应用后端,等等。大量的库和软件包提供了可在所有平台上使用的一致的API,从而进一步降低了创建真正的多平台应用程序的成本。Google Ads
和Google Assistant
等业务关键型应用程序。可靠的空安全性使类型系统更加强大,并实现了更好的性能。Dart FFI使您可以使用现有的C库以实现更好的可移植性,并可以选择对性能要求很高的任务使用经过高度调整的C代码。
自Dart 2.0引入可靠的空安全类型系统以来,空安全是Dart语言的最大补充。空安全性进一步增强了类型系统,使您能够捕获空错误,这是应用程序崩溃的常见原因。通过选择空安全性,您可以在开发过程中捕获空错误,从而防止生产崩溃。 合理的空安全性是围绕一些核心原则设计的。让我们重新审视这些原则如何影响您作为开发人员。
空安全之前的核心挑战是,您无法分辨预期传递空值的代码与不能使用空值的代码之间的区别。几个月前,我们在Flutter主渠道渠道中发现了一个错误,该错误会在某些机器配置上使各种flutter工具命令崩溃,并出现null
错误: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
可以为null
,所以major
和minor
也可以为null
。似乎很容易孤立地发现此错误,但实际上,即使是经过严格的代码审查过程(如Flutter主分支中所用的代码),这种代码也会无时无刻不在出现。出于安全考虑,静态分析会立即捕获此问题。(可以在DartPad中试用。)
那是一个非常简单的错误。在Google内部内部在代码中早期使用null安全性的过程中,我们发现了很多复杂的错误。其中一些是已经存在多年的bug,但是如果没有null安全性的额外静态检查,团队就无法找到原因。这里有一些例子:
null
的表达式的null
值。使用protobuf
的代码中最经常出现此问题,其中可选字段在未设置时返回默认值,并且永远不会为null。如此一来,通过混淆默认值和空值,代码错误地检查了默认条件。Google Pay
小组在Flutter
代码中发现了一些错误,这些错误会在尝试State在上下文之外访问Flutter
对象时失败的Widget
。在实现null
安全之前,这些对象将返回null
并掩盖错误;出于安全考虑,可靠的分析器确定这些属性永远不会为空,并引发了分析错误。Flutter
小组发现了一个错误,如果将该错误null
传递给中的scene
参数,Flutter
引擎可能会崩溃Window.render()
。在进行null
安全迁移期间,他们添加了一个提示,将Scene
标记为non-nullable
,然后能够轻松地防止可能触发null的潜在应用崩溃。一旦启用空安全,变量声明的基本改变,因为默认的类型是不可为空:
// 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之后,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
(在Flutter小部件API中经常发生)并且调用者忘记提供参数时,就会发生分析错误:
因为可靠的安全性是对我们的打字系统的根本改变,所以如果我们坚持强制采用,那将是极度破坏性的。这样,你决定到时是正确的,空安全是一项可选功能:你可以用Dart2.12,而无需被迫启用空安全。您甚至可以依赖已经启用了空安全性的软件包,无论您的应用程序或软件包是否启用了空安全性。
为了帮助您将现有代码迁移到null safety
的状态,我们提供了迁移工具和迁移指南。该工具首先分析所有现有代码。然后,您可以交互地查看该工具推断的可空性属性。如果您不同意该工具的任何结论,则可以添加可空性提示以更改推断。添加一些迁移提示可能会对迁移质量产生巨大影响。
image
目前,使用dart create
和flutter create
不启用可靠的空安全创建的新程序包和应用程序。当我们看到大多数生态系统已经迁移时,我们希望在将来的稳定版本中对此进行更改。您可以轻松的在新创建的包或应用中使用null safety
,可以使用命令dart migrate
。
在过去的一年中,我们提供了几种声音无效安全性的预览版和Beta版,目的是为生态系统植入支持无效性安全的软件包。这项准备工作很重要,因为我们建议按顺序迁移,以确保声音的安全性-您不应该在软件包或应用程序的所有依赖项都迁移之前就对其进行迁移。 我们已经发布了Dart,Flutter,Firebase和Material团队提供的数百个软件包的null安全版本。而且,我们已经从惊人的Dart和Flutter生态系统中获得了巨大的支持,因此pub.dev现在有超过一千个支持null安全的软件包。重要的是,最流行的软件包已首先迁移,因此,对于今天的发布而言,最流行的前100个软件包中的98%,前250个顶级软件包中的78%和前500个顶级软件包中的57%已及时支持零安全性。我们期待在未来几周内在pub.dev上看到更多具有空安全性的软件包。我们的分析表明,pub.dev上的绝大多数软件包已被解除阻止,可以开始迁移。
完全迁移后,Dart的null safety
就可以了。这意味着Dart 100%确保具有不可为null
的类型的表达式不能为null
。当Dart分析您的代码并确定某个变量不可为空时,该变量始终为不可为空。Dart与Swift共享可靠的安全性,但其他编程语言却很少。
Dart的null safety
的健全性还具有另一个受欢迎的含义:这意味着您的程序可以更小,更快。由于Dart确保不可为空的变量永远不会为null
,因此Dart可以进行优化。例如,Dart提前(AOT)编译器可以生成更小,更快的本机代码,因为当知道变量不是null
时,它不需要添加对null
的检查。
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支持嵌套结构。
为了声明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);
对于较大的API曲面,编写与C代码集成的Dart绑定可能非常耗时。为了减轻这种负担,我们构建了一个绑定生成器,用于根据C
头文件自动创建FFI包装器
。我们邀请您尝试一下:package:ffigen
。
随着核心FFI平台的完成,我们将重点转移到扩展FFI功能集,使其具有在核心平台之上分层的功能。我们正在调查的一些功能包括:
int
,long
,size_t
(#36140)我们已经看到Dart FFI
的许多创造性用法,以与各种基于C的API集成。这里有一些例子:
API
。它使用FFI调用Windows
,macOS
和Linux
上的本机操作系统API。Win32 API
,从而可以直接从Dart调用各种Windows API
。TensorFlow Lite API
。声音无效安全性是我们几年来对Dart语言所做的最大改变。接下来,我们将考虑在我们强大的基础上对语言和平台进行更多的增量更改。快速浏览一下我们在语言设计渠道中正在尝试的一些事情:
类型别名(#65):可以为非函数类型创建类型别名。例如,您可以创建一个typedef并将其用作变量类型:
typedef IntList = List<int>;
IntList il = [1,2,3];
三重移位运算符(#120):添加了一个新的,完全可重写的>>>
运算符,用于对整数进行无符号移位。
通用元数据注释(#1297):扩展元数据注释以也支持包含类型参数的注释。
静态元编程(#1482):支持静态元编程— Dart程序在编译期间生成新的Dart源代码,类似于Rust宏和Swift函数生成器。该功能仍处于早期探索阶段,但是我们认为它可以启用当今依赖于代码生成的用例。
Dutter 2.12
和Flutter 2.0 SDK
现已提供具有可靠的null safety
和稳定的FFI
。请花点时间查看Dart和Flutter的已知的null safety
问题。如果您发现任何其他问题,请在Dart问题跟踪器中报告这些问题。
如果您已经开发了发布在pub.dev上的软件包,请立即查看迁移指南,并了解如何迁移以达到安全性。迁移您的软件包可能会帮助解除阻止其他依赖于该软件包的软件包和应用程序。我们还要感谢已经迁移的人!
我们很想听听您在可靠安全性和FFI方面的经验。在下面发表评论或通过推特给我们@dart_lang。
原文地址:https://medium.com/dartlang/announcing-dart-2-12-499a6e689c87