# Null Safety

Course: [Dart](https://www.devtutorials.ai/learn/dart) · Section: Operators
Source: https://www.devtutorials.ai/learn/dart/operators-and-null-safety/null-safety

> What null is, how Dart stops null crashes, nullable types, the operators ?. ?? ??= and !, and late variables.

## What Is Null Safety?

**Null** = "no value at all". An empty box.

**Null safety** = Dart's rule that a variable cannot be empty unless you say it may be.

For example:

- `String name` must **always** hold text
- `String? nickname` may hold text, **or** be empty (`null`)

Dart checks this **before** your program runs.

**Simple lesson:** with null safety, Dart knows which boxes may be empty, and makes you handle them.

### A Real-World Example

A student asks the canteen for a samosa.

- The shopkeeper opens the box and hands one over.
- One day the box is **empty**, but nobody checked.
- The shopkeeper reaches in, finds nothing, and the order fails.

In older languages, using an empty value crashed the app the same way.
Dart makes you **check the box first**, so this crash cannot happen by surprise.

Every **Run** box on this page runs on the course's Dart runner in your browser.

Some boxes fail on purpose, to show the errors null safety prevents.

## What You Will Learn

In this lesson, you will learn:

1. What null safety is and why Dart added it.
2. Nullable and non-nullable types.
3. The operators `?.`, `??`, `??=` and `!`.
4. `late` and `late final` variables.
5. The most common null mistakes, and how to avoid them.

## Why Null Safety Was Introduced

The person who invented null, Tony Hoare, called it his "billion-dollar mistake".

The problem:

- Any variable could secretly be `null`.
- Using it, like `name.length`, crashed the program **while it ran**.
- These crashes often reached users, because nothing warned the programmer.

Dart's fix, added in 2021 and always on since Dart 3:

- **A normal type can never be null.** Dart checks it before running.
- **A `?` type may be null**, and Dart makes you handle that case.

**Simple lesson:** null safety moves the "empty box" crash from your user's phone to your own screen, before the app ships.

## Nullable vs Non-Nullable Types

**Non-nullable type** = a type that can never hold `null`: `String`, `int`, `bool`.

**Nullable type** = a type with `?` after it, which can also hold `null`: `String?`, `int?`.

### Non-Nullable: Never Empty

```dart runnable expect-error title="A String can never be null"
void main() {
  String name = null;
  print(name);
}
```

Dart says: `The value 'null' can't be assigned to a variable of type 'String' because 'String' is not nullable.`

### Nullable: Maybe Empty

```dart runnable title="A String? may be null"
void main() {
  String? nickname;
  print(nickname);
  nickname = 'Chintu';
  print(nickname);
  nickname = null;
  print(nickname);
}
```

- A `String?` with no value starts as `null`.
- It can switch between text and `null`.

**Simple lesson:** add `?` only when a value can really be missing.

## The Null Safety Rules

### Rule 1: You Must Check Before You Use

This code is refused before it runs:

```dart
String? nickname;
print(nickname.length);
```

Dart says: `The property 'length' can't be unconditionally accessed because the receiver can be 'null'.`

In plain words: "This box may be empty. Check it first."

### Rule 2: A Check Makes It Safe

**Type promotion** = after you check that a value is not `null`, Dart treats it as non-nullable.

```dart runnable title="Check first, then use"
void main() {
  String? nickname = 'Chintu';
  if (nickname != null) {
    print('Nickname has ${nickname.length} letters.');
  } else {
    print('No nickname.');
  }
}
```

Inside the `if` block, Dart knows `nickname` is a `String`, so `.length` is allowed.

### Rule 3: Non-Nullable Variables Need a Value Before Use

```dart
int total;
print(total);
```

Dart says: `The non-nullable local variable 'total' must be assigned before it can be used.`

**Simple lesson:** if a value may be null, check it, and Dart lets you use it after the check.

## Null Safety Operators

Checking with `if` every time is long. Dart has four short operators.

Switch the value and watch all four:

Compare:

- **`?.`** never crashes. It gives `null` when the box is empty.
- **`??`** always gives a value. It uses `'Guest'` when the box is empty.
- **`!`** crashes when the box is empty.

### ?. Null-Aware Access

**`?.`** = "if it is not null, use it. If it is null, give null."

```dart runnable title="?. gives null instead of crashing"
void main() {
  String? nickname;
  print(nickname?.length);
  nickname = 'Chintu';
  print(nickname?.length);
  print(nickname?.toUpperCase());
}
```

### ?? Null Coalescing

**`??`** = "use the left value, but if it is null, use the right value instead".

```dart runnable title="?? gives a default value"
void main() {
  String? nickname;
  print('Hello, ${nickname ?? 'Guest'}!');
  nickname = 'Chintu';
  print('Hello, ${nickname ?? 'Guest'}!');

  int? bonusMarks;
  int total = 80 + (bonusMarks ?? 0);
  print('Total: $total');
}
```

`??` is perfect with `tryParse()` (see **Data Types**): `int.tryParse(text) ?? 0`.

### ??= Null-Aware Assignment

**`??=`** = "put this value in, but **only** if the variable is null right now".

### ! Null Assertion

**`!`** = "I promise this is not null." Dart believes you.

- If you are right, it works like a normal value.
- If you are wrong, the program **crashes**.

```dart runnable expect-error title="A broken promise crashes the program"
void main() {
  int? rollNumber;
  print('Before the crash');
  print(rollNumber! + 1);
}
```

The error is `Null check operator used on a null value`.

Use `!` only when you are **sure** a value cannot be null, and Dart just cannot see why.

If you are not sure, use `?.`, `??` or an `if` check instead.

**Simple lesson:** `?.` is safe access, `??` is a default, `??=` fills an empty box, and `!` is a risky promise.

## Late Variables

### late

**`late`** = "this non-nullable variable will get a value later, before I use it".

```dart runnable title="A late variable gets its value later"
void main() {
  late String greeting;
  var hour = 9;
  if (hour < 12) {
    greeting = 'Good morning';
  } else {
    greeting = 'Good afternoon';
  }
  print(greeting);
}
```

- `greeting` is a `String`, not a `String?`. It is never null.
- `late` tells Dart to trust you about **when** it gets its value.

If you read a `late` variable before giving it a value, real Dart crashes with a `LateInitializationError`.

### late final

**`late final`** = gets a value later, and only once.

```dart runnable title="late final: set once, later"
void main() {
  late final String topper;
  var scores = 92;
  topper = scores > 90 ? 'Asha' : 'Ravi';
  print('Topper: $topper');
}
```

### Common Use Cases

- A value decided **after** some checks, like `greeting` above.
- A value that is **expensive** to work out, and maybe not needed: `late final report = buildReport();` runs `buildReport()` only the first time `report` is used.
- Fields in classes that are set up after the object is made (see **Classes**).

In real Dart, `late final x = work();` waits and runs `work()` only when `x` is first used.

In this course's runner, it runs straight away. The examples on this page do not depend on the waiting.

**Simple lesson:** `late` means "a real value is coming before I use it", so the type does not need `?`.

## Common Pitfalls

### Avoiding Null Errors

- **Prefer non-nullable types.** Add `?` only when a value can really be missing.
- **Give defaults with `??`.** `name ?? 'Guest'` always gives a value.
- **Check with `if (x != null)`.** Dart then trusts the value inside the block.
- **Avoid `!`.** Every `!` is a possible crash.
- **Use `late` only when you are sure** the value will be set before it is read.

**Easy way to remember**

- `?` on a type = "this box may be empty".
- `?.` = "peek, and give null if empty".
- `??` = "or else use this".
- `!` = "trust me", and a crash if you were wrong.

**Simple lesson:** most null bugs disappear when you use `??` and `if` checks instead of `!`.

## TL;DR

1. **null** = no value. **Null safety** = only `?` types may be null.
2. **Non-nullable** = `String`, never null. **Nullable** = `String?`, may be null.
   - Check with `if (x != null)` to use it. That is **type promotion**.
3. **Operators:**
   - `?.` = use it, or give `null`.
   - `??` = a default value. `??=` = assign only if null.
   - `!` = "not null, I promise". Crashes if wrong.
4. **`late`** = a non-nullable variable that gets its value later. **`late final`** = later, and once.
5. **Avoid null errors** = prefer non-nullable types, `??` and checks, and use `!` rarely.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| `String name = null;` | Any variable can be null | Use `String?` if it may be null | `The value 'null' can't be assigned`. |
| `nickname.length` on a `String?` | Dart will just try it | Check first, or use `?.` | `can't be unconditionally accessed`. |
| `!` everywhere | `!` fixes null errors | `!` only hides them until the program runs | `Null check operator used on a null value`. |
| `?` on everything | Nullable is safer | It forces checks everywhere | Long, messy code. |
| Reading a `late` variable too early | `late` gives a default | You must assign it first | `LateInitializationError`. |
| `x ?? y` when `x` cannot be null | `??` is always useful | It only helps nullable values | A warning, and dead code. |
| Expecting `??=` to replace a value | It always assigns | It assigns only when the value is null | The old value stays. |

## Check Yourself

## Exercises

## What to Remember

- A normal type can never be null. Add `?` when a value may be missing.
- Check with `if (x != null)`, and Dart lets you use the value safely.
- `?.` gives `null` instead of crashing, and `??` gives a default value.
- `??=` fills a variable only if it is null. `!` is a promise that crashes when wrong.
- `late` means a non-nullable value will be set before it is used.

Next: **Conditional Statements**. You will learn how a program makes decisions with if, else and switch.
