New type alias language features, improved Dart FFI

Original address: medium.com/dartlang/an…

Original author: medium.com/mit.mit

Published: May 20, 2021-8 minutes to read

By Kevin Moore and Michael Thomsen

Today, we’re releasing Dart 2.13, which features type aliases — the second most requested language feature at this point. Dart 2.13 also includes improved DFI and better performance, and we’ve also provided new official Docker graphics for Dart. This article gives an update to the null value security features introduced in version 2.12, discusses the new features in version 2.13, has some exciting news about Docker and Google Cloud support for the Dart backend, and previews some of the changes you can expect to see in future releases.

Null value security update

We rolled out full nullvalue security in Dart 2.12 in March. Null value safety is the latest major productivity feature in Dart and is designed to help you avoid null value errors, a class of errors that are often hard to spot. With the release, package publishers are encouraged to start migrating shared packages on pub.dev to Null Safety.

We are very pleased to see how quickly Null Safety has been adopted, with 93% of the top 500 most popular packages on Pub.dev already supporting Null Safety just a few months after launch. We would like to extend our heartfelt thanks to all the software package developers for getting this done so quickly and helping the entire ecosystem move forward!

With so many packages that support NULL Safety, it is likely that you will start migrating your applications to use NULL Safety. The first step is to use Dart Pub Outdated to check your application’s dependencies. See the NULL Safety Migration Guide for details. We have also modified the Dart creation and Flutter creation templates so that they are now null Safety enabled by default in new applications and packages.

Declare type alias

Type aliases are a new feature of the 2.13 language. It extends our earlier support to allow the creation of type aliases for function types, but not any other types. This highly sought after feature is the second highest rated language issue tracker.

With a type alias, you can create a new name for any existing type, which can then be used wherever the original type can be used. You don’t really define a new type, just introduce a short alias. This alias can even pass type equality tests.

typedef Integer = int;
void main() {
  print(int == Integer); // true
}
Copy the code

So what can you do with a type alias? A common use is to give a type a shorter or more descriptive name to make your code easier to read and maintain.

A good example is working with JSON (thanks to GitHub user Levi-Lesches for providing this example). Here we can define a new type alias Json that describes the Json document as a mapping from the String key to any value (using dynamic typing). We can then use this JSON type alias when defining the FromJSON-named constructor and JSON getter.

typedef Json = Map<String.dynamic>;
class User {
  final String name;
  final int age;
  User.fromJson(Json json) :
    name = json['name'],
    age = json['age'];
Json get json => {
    'name': name,
    'age': age,
  };
}
Copy the code

You can also call the constructor on a type alias named a class, so this is perfectly legal.

main() {
  var j = Json();
  j['name'] = 'Michael';
}
Copy the code

By using type aliases to name complex types, you can make it easier for readers to understand the immutability of your code. For example, the following code defines a type alias to describe a map containing keys of the generic type X and values of type List

. By giving a type a name with a single type parameter, the map’s regular structure becomes more obvious to the reader of the code.

typedef MapToList<X> = Map<X, List<X>>;
void main() {
  MapToList<int> m = {};
  m[7] = [7]; // OK
  m[8] = [2.2.2]; // OK
  for (var x in m.keys) {
    print('$x --> ${m[x]}');
  }
}

=>

7 --> [7]
8 --> [2.2.2]
Copy the code

If you try to use a mismatched type, you will get an analysis error.

m[42] = ['The', 'meaning', 'of', 'life'];

=>

The element type 'String' can't be assigned to the list type 'int'.
Copy the code

You can even use type aliases when renaming classes in public libraries. Imagine that you have an existing class PoorlyNamedClass in the public library and you want to rename it to BetterNamedClass. If you simply rename the class, your API client will suddenly get a compilation error. With type aliases, you can continue renaming, but define a new type alias for the old class name, and then annotate the old name with the @deprecated annotation. When using PoorlyNamedClass, it raises a warning, but continues to compile and work as before, giving the user time to upgrade their code. Here’s how you implement BetterNamedClass and do away with PoorlyNamedClass (in a file called myLibrary.dart).

class BetterNamedClass {... }@Deprecated('Use BetterNamedClass instead')
typedef PoorlyNamedClass = BetterNamedClass;
Copy the code

Here’s what happens when someone tries to use PoorlyNamedClass.

import 'mylibrary.dart';
voidmain() { PoorlyNamedClass p; } = >'PoorlyNamedClass' is deprecated and shouldn't be used. Use BetterNamedClass instead.
Copy the code

Type aliasing is available starting with Dart 2.13. To enable it, set the Dart SDK’s child constraint to at least 2.13 in your PubSpec.

environment:
  sdk: "> = 2.13.0 < 3.0.0"
Copy the code

This feature is backward compatible, thanks to language versioning. Packages with lower SDK constraints under 2.13 can safely reference type aliases defined in 2.13 packages, although packages prior to 2.13 cannot define their own type aliases.

Dart 2.13 FFI changes

We also have some new functionality in Dart FFI, which is our interoperability mechanism for calling C code.

First, FFI now supports structures with inline arrays (#35763). Consider a C structure with an inline array, like this.

struct MyStruct {
  uint8_t arr[8];
}
Copy the code

Now you can wrap the Dart directly and specify the element type with the Array type parameter.

class StructInlineArray extends Struct {
  @Array(8)
  external Array<Uint8> arr;
}
Copy the code

Second, FFI now supports a packaged structure (#38158). Typically, structures are laid out in memory so that members fall on address boundaries that are more accessible to the CPU. In the packaging structure, partial padding is omitted to reduce overall memory consumption, usually in a platform-specific manner. You can easily specify padding with the new @Packed(

) annotation. For example, the following code creates a structure with 4-byte alignment in memory.

@Packed(4)
class TASKDIALOGCONFIG extends Struct {
  @Uint(32)external int cbSize;
  @IntPtr(a)external int hwndParent;
  @IntPtr(a)external int hInstance;
  @Uint(32)external intdwFlags; . }Copy the code

Dart 2.13 performance changes

We are continuing to work to reduce the application size and memory footprint of the Dart code. In large Flutter applications, the internal structure representing the AOT-compiled Dart program metadata can occupy a sizeable chunk of memory. This metadata exists to enable functions such as hot overloading, interactive debugging, and formatting of human-readable stack traces that are never used in deployed applications. Over the past year, we’ve been reorganizing the Dart local runtime to eliminate this overhead as much as possible. Some of these improvements apply to all Flutter applications built in publish mode, but some require you to separate debugging information from aoT-compiled applications by using the –split-debug-info flag, thus abandoning human-readable stack traces.

Dart 2.13 includes changes that significantly reduce the space taken up by program metadata when using –split-debug-info. Take the Flutter Gallery app for example. On Android, the released APK is 112.4MB, including debugging information, but not 106.7MB (an overall reduction of 5%). This APK contains a number of assets. Just looking at the code metadata within APK, it was reduced from 5.7MB for Dart 2.12 to just 3.7MB for Dart 2.13 (a 35% reduction).

If the size and memory footprint of your application are important to you, consider using the –split-debug-info flag to omit debugging information. Note that to do this, you need to use the interest_command to make the stack trace readable again as a human being.

Official Docker support and Dart on Google Cloud

Dart is now available as an official Docker image. While Dart has provided Docker images for years, these new Dart images are tested and verified by Docker, following best practices. They also support ahead of time compilation (AOT), which can greatly reduce the size of containers built and can speed deployment in container environments — such as Cloud Runs.

While Dart is still focused on enabling application frameworks like Flutter to drive beautiful pixels on every screen, we realize that most user experiences have at least one hosting service behind them. By making it easy for Dart to build back-end services, we enable a full-stack experience that allows developers to scale their applications to the cloud using the same language and business logic as the front-end widgets.

Dart is used on the back end of a Flutter application in general, and is particularly suited for the simplicity and scalability of Cloud Run, the Google-managed serverless platform. This includes zero scaling, which means you incur no cost when your back end is not processing any requests. We worked with the Google Cloud team to provide the Functional framework for Dart, a collection of software packages, tools, and examples that make it easy to write Dart functions to deploy rather than a full server that handles HTTP requests and CloudEvents.

Check out our Google Cloud documentation to get started.

Please say a few words about the next step

We’re already making some exciting changes for the upcoming release. As always, you can follow our progress using the Language funnel tracker.

One area we are working on is providing a new set of specification lines for Dart and Flutter. Lints is a powerful way to configure Dart static analysis, but with hundreds of possible Lints that can be turned on or off, it can be difficult to decide what to choose. We are currently working to define two typical sets of gaskets that we will apply by default in the Dart and Flutter projects. We want this enabled by default in the next stable release. If you want a preview, check out the lints and Flutter_lints packages.

Finally, if you are deeply embedded with the Dart VM runtime, please note that we are planning to do away with the existing mechanism. We’ll replace it with a faster, more flexible model based on Dart FFI (see Tracking issue #45451).

Dart 2.13 is available now

Dart 2.13 with a type alias and improved FFI is available today in Dart 2.13 and Flutter 2.2 SDKs. If you’ve been waiting for your dependencies to migrate to NULL Safety, you may want to check again and use Dart Pub Outdated. 93% of the top 500 most popular packages have migrated, so there’s a good chance you’re unbanned. We also want to thank the developers who have migrated! We’d love to hear what you have to say. We’d love to hear about your experiences with the new features and changes discussed in this blog post. Leave a comment below or tweet @dart_lang.


Translation via www.DeepL.com/Translator (free version)