Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is Null Safety in Dart?

Tags:

flutter

dart

I have heard of the new Dart null safety language feature (NNBD), currently the "'non-nullable' experiment". It is supposed to introduce non-nullable by default.

The feature specification can be found here and the language GitHub issue here.

How does it work and where can I try it?

like image 674
creativecreatorormaybenot Avatar asked Feb 05 '20 03:02

creativecreatorormaybenot


People also ask

What is no null safety in flutter?

Sound null safety is what you want if possible. Dart tools automatically run your program in sound mode if the main entrypoint library of your program has opted into null safety. If you import a null-unsafe library, the tools print a warning to let you know that they can only run with unsound null safety.

What is meant by null safety?

Void safety (also known as null safety) is a guarantee within an object-oriented programming language that no object references will have null or void values. In object-oriented languages, access to objects is achieved through references (or, equivalently, pointers).

Why do we use null safety?

For us, in the context of null safety, that means that if an expression has a static type that does not permit null , then no possible execution of that expression can ever evaluate to null . The language provides this guarantee mostly through static checks, but there can be some runtime checks involved too.

Why is null safety important in flutter?

Null safety ensures that all runtime null-dereference problems are shown at compile-time. It helps you avoid many problems during development, rather than waiting until runtime to identify null errors.


1 Answers

1. Null safety / non-nullable (by default)

The null safety / non-nullable (by default), short NNBD, feature can currently be found at nullsafety.dartpad.dev.

Keep in mind that you can read the full spec here and full roadmap here. Now, sound null safety has also been officially announced for Dart.


2.1. What does non-nullable by default mean?

void main() {   String word;   print(word); // illegal    word = 'Hello, ';   print(word); // legal } 

As you can see above, a variable being non-nullable by default means that every variable that is declared normally cannot be null. Consequently, any operation accessing the variable before it has been assigned is illegal.
Additionally, assigning null to a non-nullable variable is also not allowed:

void main() {   String word;      word = null; // forbidden   world = 'World!'; // allowed } 

2.1.1. How does this help me?

If a variable is non-nullable, you can be sure that it is never null. Because of that, you never need to check it beforehand.

int number = 4;  void main() {   if (number == null) return; // redundant    int sum = number + 2; // allowed because number is also non-nullable } 

2.1.2. Remember

Instance fields in classes must be initialized if they are not nullable:

class Foo {   String word; // forbidden    String sentence = 'Hello, World!'; // allowed } 

See late below to modify this behavior.

2.2. Nullable types (?)

You can use nullable types by appending a question mark ? to a variable type:

class Foo {   String word; // forbidden    String? sentence; // allowed } 

A nullable variable does not need to be initialized before it can be used. It is initialized as null by default:

void main() {   String? word;      print(word); // prints null } 

2.2.2. !

Appending ! to any variable e will throw a runtime error if e is null and otherwise convert it to a non-nullable value v.

void main() {   int? e = 5;   int v = e!; // v is non-nullable; would throw an error if e were null    String? word;   print(word!); // throws runtime error if word is null    print(null!); // throws runtime error } 

2.3. late

The keyword late can be used to mark variables that will be initialized later, i.e. not when they are declared but when they are accessed. This also means that we can have non-nullable instance fields that are initialized later:

class ExampleState extends State {   late final String word; // non-nullable    @override   void initState() {     super.initState();      // print(word) here would throw a runtime error     word = 'Hello';   } } 

Accessing word before it is initialized will throw a runtime error.

2.3.1. late final

Final variables can now also be marked late:

late final int x = heavyComputation(); 

Here heavyComputation will only be called once x is accessed. Additionally, you can also declare a late final without an initializer, which is the same as having just a late variable, but it can only be assigned once.

late final int x; // w/e x = 5; // allowed x = 6; // forbidden 

Note that all top-level or static variables with an initializer will now be evaluated late, no matter if they are final.

2.4. required

Formerly an annotation (@required), now built-in as a modifier. It allows to mark any named parameter (for functions or classes) as required, which makes them non-nullable:

void allowed({required String word}) => null; 

This also means that if a parameter should be non-nullable, it needs to be marked as required or have a default value:

void allowed({String word = 'World'}) => null;  void forbidden({int x}) // compile-time error because x can be null (unassigned)     =>     null; 

Any other named parameter has to be nullable:

void baz({int? x}) => null; 

2.5. ?[]

The null aware ?[] operator was added for the index operator []:

void main() {   List<int>? list = [1, 2, 3];    int? x = list?[0]; // 1 } 

See also this article about the syntax decision.

2.5.1. ?..

The cascade operator now also has a new null aware operator: ?...

It causes the following cascade operations to only be executed if the recipient is not null. Therefore, the ?.. has to be the first cascade operator in a cascade sequence:

void main() {   Path? path;    // Will not do anything if path is null.   path     ?..moveTo(3, 4)     ..lineTo(4, 3);    // This is a noop.   (null as List)     ?..add(4)     ..add(2)     ..add(0); } 

2.6. Never

The following explanation sucks. Read "Top and bottom" from "Understanding null safety" for a good one.

To avoid confusion: this is not something that developers have to worry about. I want to mention it for the sake of completeness.

Never is going to be a type like the previously existing Null (not null) defined in dart:core. Both of these classes cannot be extended, implemented, or mixed in, so they are not intended to be used.

Essentially, Never means that no type is allowed and Never itself cannot be instantiated.
Nothing but Never in a List<Never> satisfies the generic type constraint of the list, which means that it has to be empty. List<Null>, however, can contain null:

// Only valid state: [] final neverList = <Never>[   // Any value but Never here will be an error.   5, // error   null, // error    Never, // not a value (compile-time error) ];  // Can contain null: [null] final nullList = <Null>[   // Any value but Null will be an error.   5, // error   null, // allowed    Never, // not a value (compile-time error)   Null, // not a value (compile-time error) ]; 

Example: the compiler will infer List<Never> for an empty const List<T>.
Never is not supposed to be used by programmers as far as I am concerned. (I was wrong).

3. Learn more

You can read the official article on sound null safety.
Furthermore as mentioned at the beginning, you can play with it on DartPad.

like image 53
creativecreatorormaybenot Avatar answered Oct 14 '22 17:10

creativecreatorormaybenot