Swift Optional vs Dart sound null safety
Recently the Dart team announced sound null safety. Dart was already a type-safe language, and now it is even better.
Sound null safety means that if a variable is not declared as nullable, then, whenever you access that variable, you will never find null, for its entire lifetime. This should remind you of Swift Optional
.
Dart sound null safety and Swift Optional
have a lot in common, but they are implemented in different ways. In Swift, Optional
is a generic type, which means that, when we write var x: String?
, what we mean is var x: Optional<String>
: x
is a variable that can contain a String
or nil
.
The Dart team took a different approach.
When you write String? x
, the meaning is the same, x
is a variable that can contain a String
or null
, but String?
is a union type, it describes a value that can be one of several types.
In Dart, String
is called a non-nullable type, while String?
is a nullable type. In the rest of the article, we are going to refer to nullable types in Dart and to Optional
in Swift as optionals, or optional types.
The two implementations are different, but they have a lot in common.
Optional binding
In Swift you can bind the value of an optional to a new variable, if that value is not nil
, using an if let
statement or a guard let
:
// Swift
var x: String?
... // Assign some values or nil to x.
// print(x.count) // This does not compile.
guard let nonOptionalX = x else {
return
}
// nonOptionalX is of type String, not String?.
print(nonOptionalX.count) // This will compile.
In Dart, the same behavior can be obtained by simply checking if the variable is null
:
// Dart
String? x;
... // Assign some values or null to x.
// print(x.length); // This would NOT compile.
if (x == null) {
// x is `null`
return;
}
// x is non-null, and the compiler knows that.
print(x.length); // This will compile.
Optional chaining
Optional chaining is used to safely access properties or methods of an optional, and it is the same in both languages. It was already available in Dart before the introduction of sound null safety.
// Swift
var x: String?
... // Assign some values or `nil` to x.
// let length = x.count // This does not compile.
let length = x?.count
// length is an Int? and it contains either nil, or the length of the string in x
// Dart
String? x;
... // Assign some values or null to x.
// var length = x.length; // This would NOT compile.
var length = x?.length;
// length is an Int? and it contains either null, or the length of the string in x
Nil-coalescing operator
As per the optional chaining, the nil-coalescing operator is available in both languages, and it was also available before the introduction of sound null safety.
// Swift
var x: Bool?
... // Assign some values or `nil` to x.
print(x ?? false) // prints either the value in x, or 'false' if x is nil.
// Dart
bool? x;
... // Assign some values or null to x.
print(x ?? false); // prints either the value in x, or 'false' if x is nil.
late
keyword
In Dart, you can use the late
keyword in front of a non-nullable property, to inform the compiler that that property is not initialized immediately, but you will assigne a non-null value to it before accessing it.
From the official article by the Dart team:
// Dart
class Goo {
late Viscosity v;
Goo(Material m) {
v = m.computeViscosity();
}
}
In my opinion, you should avoid this use of late
as much as possible. Use an Optional instead. The code might be clear to you while you write it, but it lacks in maintainability.
You can also use late
on a property that has an initializer:
// Dart
class Goo {
late Viscosity v = _m.computeViscosity();
}
In this case, the property v
becomes lazy and it is initialized only when it is accessed for the first time. This is the same as using lazy
in Swift:
// Swift
class Goo {
lazy var v = m.computeViscosity()
}
Extending the optional type
In both languages, we can extend the functionalities of the optional type:
// Swift
extension Optional where Wrapped == Bool {
/// Returns the value, or `false` if the value is `nil`
func orFalse() -> Bool {
return self ?? false
}
}
let x: Bool? = nil
print(x.orFalse()) // prints `false`.
// Dart
extension UnwrapOrFalse on bool? {
/// Returns the value, or `false` if the value is `null`
bool orFalse() {
return this ?? false;
}
}
bool? x;
print(x.orFalse()); // prints `false`.
How programming in Dart will change
I am a big fan of Swift Optional
. I like that the developer is forced to specified when a certain variable or parameter could be nil
. Having a very similar feature in Dart will be welcomed by the entire community.
Say goodbye to all those, now useless, asserts on initializers and method definitions.
// Dart
Article({this.id, this.description}):
assert(id != null),
assert(description != null);
Leave a comment