In Dart the null-aware operator for methods in combination with the ?? operator does not work well.
Image having a call chain like this:
object.getter.getter.getter.getter
Now when object could be null and I do not want an exception I have to write this:
object?.getter?.getter?.getter?.getter
That is a null check for every getter, even though only object can be null, but then the getter's obviously do not work.
I am okay with this. But now I have a scenario, where I want to return another value if null, but I am working with a list:
list[index][index][index]
How do I use the null-aware operator on the List getter?
This does not work:
list?[index]
and this does not exist for List's in Dart.
list?.get(index)
I want to achieve something like this:
list?[index]?[index]?[index] ?? 0
Where I know that only list will ever be null.
This long code would work:
list == null ? 0 : list[index][index][index]
There is no null-aware operator for the indexing operator ([])
You can use elementAt() instead:
list?.elementAt(index)?.elementAt(index)?.elementAt(index) ?? 0
UPDATE - 2021/05/24
If you are using null-safety, you can now use this:
list?[index]?[index]?[index] ?? 0
Related
Can someone simply explain to me how null assertion (!) works and when to use it?
The ! operator can be used after any expression, e!.
That evaluates the expression e to value v, then checks whether v is the null value. If it is null, an error is thrown. If not, then e! also evaluates to v.
The static type of an expression e! is (basically) the static type of e with any trailing ?s remove. So, if e has type int?, the type of e! is int.
You should not use e! unless e can be null (the type of e is potentially nullable).
The ! operator is dynamically checked. It can throw at runtime, and there is no static check which can guarantee that it won't. It's like using a value with type dynamic in that all the responsibility of preventing it from throwing is on the author, the compiler can't help you, and you need good tests to ensure that it won't throw when it's not supposed to.
It's called an assertion because it should never throw in production code.
So, use e! when you know (for some reason not obvious to the compiler, perhaps because of some invariant guaranteeing that the value is not null while something else is true) that e is not null.
Example:
abstract class Box<T extends Object> {
bool hasValue;
T? get value;
}
...
Box<int> box = ...;
if (box.hasValue) {
var value = box.value!;
... use value ...
}
If you are repeatedly using ! on the same expression, do consider whether it's more efficient to read it into a local variable just once.
Also, if (like this Box example) the value being null is equivalent to the other test you just did, maybe just check that directly:
Box<int> box = ...;
var value = box.value;
if (value != null) {
... use value ...
}
This code, with an explicit != null check on a local variable, is statically guaranteed to not throw because the value is null.
The code using ! above relies on the author to maintain whichever invariant allowed them to write the !, and if something changes, the code might just start throwing at runtime. You can't tell whether it's safe just by looking at the code locally.
Use ! sparingly, just like the dynamic type and late declarations, because they're ways to side-step the compiler's static checking and ensure it that "this is going to be fine". That's a great feature when you need it, but it's a risk if you use it unnecessarily.
Dart null safety says that by default all variables of primitive data types (int, String, bool) are non-nullable, they can't have a null value, right? Let suppose if I have a variable 'a' of type int then I can't assign it a null value unless to use question mark with data type.
int a=null;
the above code will generate an error like 'The value 'null' can't be assigned to a variable of type 'int' because 'int' is not nullable.'
But at the same time, dart says that all initialized variables have a default value of null. My question is why statement-1 doesn't match with statement-2? Dart should not allow us to declare an uninitialzed variables because uninitialzed variables contain null value which doesn't apply with its own statement. Can anybody clear my confusion?
if you declare variables like this
int? a = null;
int? a;
Both will be null;
when you put '?' after variable you are saying to dart "hey I'm okay if it's null or not"
but sometimes you won't execute some function if the variable passing to it is null so you have to declare a variable without '?' in this case you are saying to dart "hey this variable must not be null";
also, you have to do research about null checkers, because it's very important
The ?, !, ?? null checkers
I'll keep it very simple
? says It's ok if the value is null
! says it must have non null value
?? says if it's null then assign another value...
String? nullableValue;
String nonNullableValue;
nonNullableValue = nullableValue; //throws error because nullable value cants be assigned to non nullable variable;
nonNullableValue = nullableValue ?? "undefined or null"// You are saying to dart "hey if nullable value null then assign "undefined or null";
nonNullableValue = nullableValue! // You are saying to dart "Hey this value Must not be null . But you have to check value for null with if condition like this
if(nullableValue !=null) {
nonNullableValue = nullableValue!;// By putting ! you are saying "don't worry dart this is checked value and its 100% not null";
}
The main reason for null safety is checking or preventing a variable to be null
Please read official docs for more information
In nullsafety.dartpad.dev if I write the following code:
void main() {
String? name = 'Bob';
print(name.length);
}
I get the following compile-time error:
An expression whose value can be 'null' must be null-checked before it can be dereferenced
And the following runtime error:
Property 'length' cannot be accessed on 'String?' because it is potentially null.
The Type promotion on null checks documentation says:
The language is also smarter about what kinds of expressions cause promotion. An explicit == null or != null of course works. But explicit casts using as, or assignments, or the postfix ! operator we’ll get to soon also cause promotion. The general goal is that if the code is dynamically correct and it’s reasonable to figure that out statically, the analysis should be clever enough to do so.
Question
There is no possible way name could be null in the code above. The documentation also says assignments should cause type promotion. Am I misunderstanding type promotion or is this a bug in DartPad?
Clarification
Since a couple of the answers are providing workaround solutions to the error messages, I should clarify that I'm not trying to solve the coding problem above. Rather, I'm saying that I think the code should work as it it. But it doesn't. Why not?
This answer is in response to the bounty that was added to the original question. The bounty reads:
Please explain how String? is different from String and how type
promotion works in Dart.
String? vs String
The type String? can contain a string or null. Here are some examples:
String? string1 = 'Hello world';
String? string2 = 'I ❤️ Dart';
String? string3 = '';
String? string4 = null;
The type String, on the other hand, can only contains strings (once null safety is a part of Dart, that is). It can't contain null. Here are some examples:
String string1 = 'Hello world';
String string2 = 'I ❤️ Dart';
String string3 = '';
If you try to do the following:
String string4 = null;
You'll get the compile-time error:
A value of type 'Null' can't be assigned to a variable of type 'String'.
The String type can't be null any more than it could be an int like 3 or a bool like true. This is what null safety is all about. If you have a variable whose type is String, you are guaranteed that the variable will never be null.
How type promotion works
If the compiler can logically determine that a nullable type (like String?) will never be null, then it converts (or promotes) the type to its non-nullable counterpart (like String).
Here is an example where this is true:
void printNameLength(String? name) {
if (name == null) {
return;
}
print(name.length);
}
Although the parameter name is nullable, if it actually is null then the function returns early. By the time you get to name.length, the compiler knows for certain that name cannot be null. So the compiler promotes name from String? to String. The expression name.length will never cause a crash.
A similar example is here:
String? name;
name = 'Bob';
print(name.length);
Although name is nullable here, too, the string literal 'Bob' is obviously non-null. This also causes name to be promoted to a non-nullable String.
The original question was regarding the following:
String? name = 'Bob';
print(name.length);
It seems that this should also promote name to a non-nullable String, but it didn't. As #lrn (a Google engineer) pointed out in the comments, though, this is a bug and when null safety comes out, this will also work like the previous example. That is, name will be promoted to a non-nullable String.
Further reading
Sound null safety
Type promotion on null checks
I understand what you are saying. Try this out.
In order for type promotion to work you must first confirm that the value is not null as the documentation says.
As you can see in the picture dart is able to do the type promotion or understand that name is not going to be null because it checks that on the if statement beforehand.
But if using it outside the if statement without checking if it is not null beforehand, dart knows it can be assigned null anytime again. That’s why it encourages always checking if it is null. Because any instatiated variable ( a variable with a value assigned) can be assigned null in the future.
What's the difference between ? and ! when used in a collection in Dart?
Say, I have:
var list = [1, 2];
Now, I can either use
print(list?[0]); // prints 1
or
print(list![0]); // also prints 1
Both of them seems to do the same job, so what's the difference?
Both of them seem to do the same job because your list is of type List<int> (non-nullable) and not the List<int>? (nullable). If your list had been of nullable type like:
List<int>? list;
you'd see the difference.
Using? (Null aware operator)
It would be safe to use ? because in case list is null, list?[0] would still print null rather than throwing an error.
print(list?[0]); // Safe
or you could also use ?? to provide a default value.
print(list?[0] ?? -1); // Safe. Providing -1 as default value in case the list is null
Using ! (Bang operator)
However, ! throws runtime error because you're explicitly saying that your list is not null and you're downcasting it from nullable to non-nullable:
print(list![0]); // Not safe. May cause runtime error if list is null
This is equivalent to
print((list as List)[0]);
Is there a way ensure null safety in a GORM dynamic finder?
If I wanted to do something like Book.findByName(author.bookname), I could guard against null authors by changing it to Book.findByName(author?.bookname) but how do I avoid an exception on nulls?
Book.findByName() is applicable for argument types: () values: []
auther?.bookname ? Book.findByName(author.bookname) : null //or do nothing
Based on the fact from the example that bookname is NOT NULL.
or you can use find
Book.find {name == author?.bookname} //Returns null if no records found