List of Programming Languages with Null Safety

Share
For reference, some programming languages I know that feature null safety, meaning there is a language level syntax to declare whether a variable is nullable and accessing a nullable variable without checking if it's null first results in a build time error (or compile time error, or edit time error, or whatever the language calls it).

Typescript

Typescript transpiles to Javascript adding static type checking and other features to the language. Typescript has strict null checks1—they are optional and can be enabled in the tsconfig.json file. Syntax:

// declaration
let foo: number | null = null;

// escape hatch with exclamation point,
// this would result in a runtime error in a sane programming language
// but it's Javascript so bar will become 5 because null + 5 is 5.
const bar = foo! + 5;

Kotlin

Kotlin has null safety. It's typescript for Java. According to the documentation, the following code will result in a compile-time error:2

var b: String? = "abc"

// Re-assigns null to the nullable variable
b = null

// Tries to directly return the length of a nullable variable
val l = b.length

Dart

Dart has null safety3. It's a programming language for creating cross-platform (desktop and mobile) GUI applications. A nullable variable can be promoted to non-nullable simply by checking it through static flow analysis. For example:

String? optionalString = null;
if (optionalString != null) {
    print(optionalString);
}

This promotion doesn't work on classes when a member isn't private and final, since subclasses could modify the value of a member variable between checking it and trying to use it. To be honest, I'd expect this to be possible is simple cases if no virtual methods are called. This means that in order to use a nullable public member variable you have to transfer its value to a local variable first. For example:

class Coffee {
  String? _temperature;

  void checkTemp() {
    var temperature = _temperature;
    if (temperature != null) {
      print('Ready to serve ' + temperature + '!');
    }
  }
}

Swift

Swift has null safety, except null is called nil. It seems to target mainly Apple operating systems. I'm not really sure what it can do. The code from the documentation:4

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}

Rust

Rust has null safety but no nulls. It calls them "options" instead but it's essentially the same thing. Rust is a low-level programming language that can be compiled to Web Assembly. The code from the documentation:5

fn divide(numerator: f64, denominator: f64) -> Option<f64> {
    if denominator == 0.0 {
        None
    } else {
        Some(numerator / denominator)
    }
}

// The return value of the function is an option
let result = divide(2.0, 3.0);

// Pattern match to retrieve the value
match result {
    // The division was valid
    Some(x) => println!("Result: {x}"),
    // The division was invalid
    None    => println!("Cannot divide by 0"),
}

Zig

Zig has null safety6. Zig is an unstable low-level programming language that can just use C headers directly, so you could just import SDL.h and start making a game with it if you wanted. Nullable variables are called "optionals." For example:

const optional_number: ?i32 = null;
const forty_two: i32 = optional_number orelse 42;

Whiley

Whiley has null safety. More specifically, you can't use a variable of the union type null|int, but when you check if it's not null, Whiley changes the type of the variable to int and then you can use it7. Whiley is an unstable functional language with formal static verification that somehow transpiles to Javascript.

import std.array

function indexOf(int[] items, int item) -> null|int:
    int i = 0
    while i < |items|:
        if items[i] == item:
            return i
        i = i + 1
    return null

function split(int[] items, int item) -> int[][]:
    int|null idx = indexOf(items,item)
    // idx has type null|int
    if idx is int:
        // idx now has type int
        int[] below = array.slice(items,0,idx)
        int[] above = array.slice(items,idx,|items|)
        return [below,above]
    else:
        // idx now has type null
        return [items] // no occurrence

Observations

C# has a syntax for declaring a nullable value type such as a number using a question mark (?)8. This isn't the same thing as having null safety. A "value type" is a type that is passed by value instead of reference, as opposed to a "reference type." Reference types can be set to null, while value types can't. Boxing a value type inside a Nullable<T> simply creates a nullable reference type that contains a value type of type T. C# won't stop you from trying to access a variable of reference type that may be null, nor does it provide any way for declaring a reference type variable as nullable (they are all nullable).

Null safety is the best thing since sliced bread: the number of times I accessed a null on even the simplest programs is just ridiculous. There is just no way I can remember to check if every nullable variable is null without a compiler warning me about it. It's always going to be: run the program, the program crashes, oh wait I forgot a null check there. Run it again, it crashes on a different method of the same class because because the same nullable property was null again, and this is going to happen for every single method on every single project. As if that wasn't bad enough, nobody ever documents "this can't return a null." That doesn't make sense, does it? Why would you document that? Just document the functions that DO return null. Except that consumers of a library will be left wondering if those functions without this assertion really never return null or the library developer just forgot to document it. Null safety doesn't just document the variables that are nullable, it also documents the variables that are NOT nullable. It's great and every modern programming language should have it.

Quotes

The Dart language enforces sound null safety.

Null safety prevents errors that result from unintentional access of variables set to null.

For example, if a method expects an integer but receives null, your app causes a runtime error. This type of error, a null dereference error, can be difficult to debug.

With sound null safety, all variables require a value. This means Dart considers all variables non-nullable. You can assign values of the declared type only, like int i=42. You can never assign a value of null to default variable types. To specify that a variable type can have a null value, add a ? after the type annotation: int? i. These specific types can contain either a null or a value of the defined type.

Sound null safety changes potential runtime errors into edit-time analysis errors. 

https://dart.dev/null-safety (accessed 2025-04-12)

Null safety is a Kotlin feature designed to significantly reduce the risk of null references, also known as The Billion-Dollar Mistake.

One of the most common pitfalls in many programming languages, including Java, is that accessing a member of a null reference results in a null reference exception. In Java, this would be the equivalent of a NullPointerException, or an NPE for short.

Kotlin explicitly supports nullability as part of its type system, meaning you can explicitly declare which variables or properties are allowed to be null. Also, when you declare non-null variables, the compiler enforces that these variables cannot hold a null value, preventing an NPE.

Kotlin's null safety ensures safer code by catching potential null-related issues at compile time rather than runtime. This feature improves code robustness, readability, and maintainability by explicitly expressing null values, making the code easier to understand and manage.

https://kotlinlang.org/docs/null-safety.html (accessed 2025-04-12)

This separation of optional and non-optional values lets you explicitly mark what information can be missing, and makes it easier to write code that handle missing values. You can’t accidentally treat an optional as if it were non-optional because this mistake produces an error at compile time. After you unwrap the value, none of the other code that works with that value needs to check for nil, so there’s no need to repeatedly check the same value in different parts of your code.

When you access an optional value, your code always handles both the nil and non-nil case.

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics#nil (accessed 2025-04-12)

Instead of integers, let's talk about pointers. Null references are the source of many runtime exceptions, and even stand accused of being the worst mistake of computer science.

Zig does not have them.

https://ziglang.org/documentation/master/#Optionals (accessed 2025-04-12)

Null References. In many languages (e.g. C/C++, Java, etc) the use of null is a significant source of error[3]. For example, in Java dereferencing the null value gives rise to a NullPointerException, which is regarded as the most common form of error in Java[? ]. The issue is that, in such languages, one can treat nullable references as though they are non-null references[4]. In the research literature, there have been many proposals to solve this problem using static type systems[5;6;7;8;9;10;11;12]. Unfortunately, at the time of writing, very few languages have incorporated such ideas.

https://whiley.org/pdfs/GettingStartedWithWhiley.pdf (accessed 2025-04-12)

References

Written by Noel Santos.

About the Author

I'm a self-taught Brazilian programmer graduated in IT from a FATEC. In a world of increasingly complex and essential computers, I decided to use my technical expertise in hardware, desktop applications, and web technologies to create an informative resource to make PC's easier to understand.

View Comments