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
Related
I'm new at Dart and I'm taking a course but I don't know if I'm doing something wrong at this particular lesson. I have almost the exact code as my instructor, yet I get an Error instead of a "null" result. This is the code:
import 'package:hello_dart/hello_dart.dart' as hello_dart;
// we can also add exceptions on the parameters using "[]"
//
void main(List<String> arguments) {
var name = sayHello("Pink", "Unicorn");
print(name);
}
String sayHello(String name, String lastName, [int age]) => "$name "
"$lastName $age";
The idea of this lesson is to create exceptions on the function's parameters. On this example, he adds [] on "int age" to add an exception and erases "$age". With that he gets "Pink Unicorn null" as a result.
But I get instead this error:
The parameter 'age' can't have a value of 'null' because of its type 'int', but the implicit default value is 'null'.
Try adding either an explicit non-'null' default value or the 'required' modifier.
String sayHello(String name, String lastName, [int age])
^^^
The course is at least four years old, so maybe there was an update where Dart no longer gives a "null" result to an "int" value ? or am I doing something wrong ? https://prnt.sc/cq-YmQgPVx1R
Yes, this course is outdated, it missed dart-null-safety.
Those are optional positional parameters. Please do not call that "exception", since "exception" is a word generally used for something totally unrelated in almost all programming languages including Dart.
Since an int can be longer be null, your optional positional parameter needs to be of type int? to be able to have null as a value.
So this:
void main(List<String> arguments) {
var name = sayHello("Pink", "Unicorn");
print(name);
}
String sayHello(String name, String lastName, [int? age]) => "$name "
"$lastName $age";
Will produce the output:
Pink Unicorn null
For more information on "null safety" and why it's a really great feature, see https://dart.dev/null-safety
Try to use tutorials from the last year at least. Flutter and Dart change fast, to the better, and you should not start learning something only to have outdated knowledge when you are done. Make sure you are learning from a source that is current.
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.
In JS, there is undefined and null. Undefined means "no value", null means "value equivalent to emptiness".
In Dart however (and possibly in other languages, I don't know, but right now I'm using Dart), there is no undefined, only null. Therefore it is impossible to make the distinction between a value equivalent to emptiness and the absence of value.
Is there a standard way of simulating this difference in Dart?
No. The null value (of type Null) is the only built-in value that represents the absence of a value. It does not distinguish on why the value is absent.
It's also (almost) the only type you can combine with another type directly in the type system. With null safety, you'll be able to write int? for int-or-Null.
If you want another marker for absence, you can use (or write) an Option type like:
abstract class Option<T> {
final T value;
bool get hasValue => true;
Option(this.value);
factory Option.none() => const _OptionNone();
}
class _OptionNone implements Option<Never> {
const _OptionNone();
bool get hasValue => false;
T get value => throw UnsupportedError("None option has no value");
}
Then you can represent either value or no value.
(There are existing implementations of such a class, for example in the quiver package).
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]);