Understanding difference between int? and int (or num? and num) [duplicate] - dart

This question already has answers here:
What is Null Safety in Dart?
(2 answers)
Closed 1 year ago.
After defining a map (with letters as keys and scrabble tile scores as values)
Map<String, int> letterScore //I'm omitting the rest of the declaration
when I experiment with this function (in DartPad)
int score(String aWord) {
int result = 0;
for (int i = 0; i < aWord.length; ++i) {
result += letterScore[aWord[i]];
}
return result;
}
I consistently get error messages, regardless of whether I experiment by declaring variables as num or int:
Error: A value of type 'int?' can't be assigned to a variable of type
'num' because 'int?' is nullable and 'num' isn't [I got this after declaring all the numerical variables as int]
Error: A value of type 'num' can't be returned from a function with
return type 'int'.
Error: A value of type 'num?' can't be assigned to a variable of type
'num' because 'num?' is nullable and 'num' isn't.
I understand the difference between an integer and a floating point (or double) number, it's the int vs int? and num vs num? I don't understand, as well as which form to use when declaring variables. How should I declare and use int or num variables to avoid these errors?

Take this for example:
int x; // x has value as null
int x = 0; // x is initialized as zero
Both the above code are fine and compilable code. But if you enable Dart's null-safety feature, which you should, it will make the above code work differently.
int x; // compilation error: "The non-nullable variable must be assigned before can be used"
int x = 0; // No Error.
This is an effort made from the compiler to warn you wherever your variable can be null, but during the compile time. Awesome.
But what happens, if you must declare a variable as null because you don't know the value at the compile time.
int? x; // Compiles fine because it's a nullable variable
The ? is a way for you tell the compiler that you want this variable to allow null. However, when you say a variable can be null, then every time you use the variable, the compiler will remind you to check whether the variable is null or not before you can use it.
Hence the other use of the ?:
int? x;
print(x?.toString() ?? "0");
Further readings:
Official Docs: https://dart.dev/null-safety/understanding-null-safety
Null-aware operators: https://dart.dev/codelabs/dart-cheatsheet

Related

Dart Operators : The name 'b' isn't a type and can't be used in an 'is' expression [duplicate]

This question already has answers here:
What is the difference between 'is' and '==' in Dart?
(2 answers)
Closed 11 months ago.
I'm new to dart and reading about dart operators. In the book fluter in Action by Eric Windmill (Chapter 2, 2.24, page 34) the auther says:
is and is! verify that two objects are of the same type. They are equivalent to == and !=.
Trying to implement this as shown in the code below
void main() {
int a = 7;
int b = 2;
bool z = a == b; // It works when I use the equals symbol
print('Result: $z');
}
But when I use the ```is`` keyword, I get an error
void main() {
int a = 7;
int b = 2;
bool z = a is b; // It doesn't work here
print('Result: $z');
}
Error
The name 'b' isn't a type and can't be used in an 'is' expression.
Try correcting the name to match an existing type.
Not sure what the context of that statement is but is and is! is not the same as == and != in the sense that they does the same. I guess what the want to explain, is that the opposite of is is is! like the opposite of == is !=.
== is used to check if two objects are equal (based on what this object at left side defines to be equal). So for int we return true when using == if both numbers have the same numeric value.
The is operator is used to test if a given object does implement a given interface. An example:
void main() {
Object myList = <int>[];
if (myList is List<int>) {
print('We have a list of numbers.');
}
}
The error you are getting are telling you that for using is you need to provide an actual type at the right side of the is and not an object instance.

Why compiler assuming `json['x'] = int?`?

class Point {
int x;
int y;
Point(this.x, this.y);
Point.zero()
: x = 0,
y = 0;
Point.fromJson({required Map<String, int> json})
:x = json['x'], //Error : `The initializer type 'int?' can't be assigned to the field type 'int'`
y = json['y']; // Error :`The initializer type 'int?' can't be assigned to the field type 'int'`
}
As you can see the argument json here is Map<String , int> so why am getting this error here.When both are non-nullable here ?
Why compiler assuming json['x'] = int? ?
Because the [] operator on Map returns a nullable by spec:
V? operator [](Object? key)
The value for the given key, or null if key is not in the map.
https://api.dart.dev/stable/2.15.1/dart-core/Map/operator_get.html
So if you are asking for a key that is not in your Map you will get a null value back and not an exception.
If you are 100% sure json['x'] will always work and want the application to crash in case this is not the case, you can use json['x']!. Alternative, you need to provide default values or other type of handling in case these values is not in the map.

printing variable out of scope does not yield error

I had this weird problem in Dart. Consider the following code :
class Number {
int num = 10;
}
Here, I created a little class with a int object num
When I try to print it using the main() function OUTSIDE the class like :
main() {
print(num);
}
I get the output as :
num
Which is weird, since I expected an error. If I were to print a undefined variable as in print(foo); I would get an error, which is expected.
What I find even more interesting is the runtimeType of a variable whose value is num.
var temp = num;
print(temp.runtimeType);
}
The above code prints _Type, when I expected it to be int.
Can somebody please clear this?
The name num is a type declared in dart:core. It's the supertype of int and double.
When you do print(num); outside the scope where your int num; variable is declared, the num refers to that type from dart:core which is always imported and therefore in scope.
Dart type names can be used as expressions, they evaluate to a Type object.
So, you are printing a Type object for the type num, which prints "num", and the run-time type of that object, which is again a Type object, which prints _Type because that's the actual internal type of the Type object instance.

Why can't I convert a Number into a Double?

weight is a field (Number in Firestore), set as 100.
int weight = json['weight'];
double weight = json['weight'];
int weight works fine, returns 100 as expected, but double weight crashes (Object.noSuchMethod exception) rather than returning 100.0, which is what I expected.
However, the following works:
num weight = json['weight'];
num.toDouble();
When parsing 100 from Firestore (which actually does not support a "number type", but converts it), it will by standard be parsed to an int.
Dart does not automatically "smartly" cast those types. In fact, you cannot cast an int to a double, which is the problem you are facing. If it were possible, your code would just work fine.
Parsing
Instead, you can parse it yourself:
double weight = json['weight'].toDouble();
Casting
What also works, is parsing the JSON to a num and then assigning it to a double, which will cast num to double.
double weight = json['weight'] as num;
This seems a bit odd at first and in fact the Dart Analysis tool (which is e.g. built in into the Dart plugin for VS Code and IntelliJ) will mark it as an "unnecessary cast", which it is not.
double a = 100; // this will not compile
double b = 100 as num; // this will compile, but is still marked as an "unnecessary cast"
double b = 100 as num compiles because num is the super class of double and Dart casts super to sub types even without explicit casts.
An explicit cast would be the follwing:
double a = 100 as double; // does not compile because int is not the super class of double
double b = (100 as num) as double; // compiles, you can also omit the double cast
Here is a nice read about "Types and casting in Dart".
Explanation
What happened to you is the following:
double weight;
weight = 100; // cannot compile because 100 is considered an int
// is the same as
weight = 100 as double; // which cannot work as I explained above
// Dart adds those casts automatically
You can do it in one line:
double weight = (json['weight'] as num).toDouble();
You can Parse the data Like given below:
Here document is a Map<String,dynamic>
double opening = double.tryParse(document['opening'].toString());
In Dart, int and double are separate types, both subtypes of num.
There is no automatic conversion between number types. If you write:
num n = 100;
double d = n;
you will get a run-time error. Dart's static type system allows unsafe down-casts, so the unsafe assignment of n to d (unsafe because not all num values are double values) is treated implicitly as:
num n = 100;
double d = n as double;
The as double checks that the value is actually a double (or null), and throws if it isn't. If that check succeeds, then it can safely assign the value to d since it is known to match the variable's type.
That's what's happening here. The actual value of json['weight'] (likely with static type Object or dynamic) is the int object with value 100. Assigning that to int works. Assigning it to num works. Assigning it to double throws.
The Dart JSON parser parses numbers as integers if they have no decimal or exponent parts (0.0 is a double, 0e0 is a double, 0 is an integer). That's very convenient in most cases, but occasionally annoying in cases like yours where you want a double, but the code creating the JSON didn't write it as a double.
In cases like that, you just have to write .toDouble() on the values when you extract them. That's a no-op on actual doubles.
As a side note, Dart compiled to JavaScript represents all numbers as the JavaScript Number type, which means that all numbers are doubles. In JS compiled code, all integers can be assigned to double without conversion. That will not work when the code is run on a non-JS implementation, like Flutter, Dart VM/server or ahead-of-time compilation for iOS, so don't depend on it, or your code will not be portable.
Simply convert int to double like this
int a = 10;
double b = a + 0.0;

Why does `if (var = null)` compile in dart?

I've recently came across this question How do I solve the 'Failed assertion: boolean expression must not be null' exception in Flutter
where the problem comes from a should be invalid code that gets treated as valid.
This code can be summarized as :
int stuff;
if (stuff = null) { // = instead of ==
}
But why does this code compiles ? As the following will not.
int stuff;
if (stuff = 42) {
}
With the following compile error :
Conditions must have a static type of 'bool'.
So I'd expect out of consistency that if (stuff = null) to gives the same error.
null is a valid value for a bool variable in Dart, at least until Dart supports non-nullable types.
bool foo = null;
or just
bool foo;
is valid.
Therefore in the first case there is nothing wrong from a statical analysis point of view.
In the 2nd case the type int is inferred because of the assignment, which is known to not be a valid boolean value.
bool foo = 42;
is invalid.
When you say var stuff; with no initial value it is giving stuff a static type of dynamic. Since dyamic might be a bool, it's legal to assign null to a variable of type dynamic, and it's legal to use a possibly null bool in a conditional, the compiler doesn't flag this. When you say int stuff; the compiler knows that stuff could not be a bool. The reported error in that case is cause by the static type of stuff, not the assignment to null.
Edit: Got the real answer from someone who knows how to read the spec.
The static type of an assignment expression is the right hand side of the assignment. So the expression stuff = null has the static type of Null which is assignable to bool.
The reasoning is that the value of an assignment is the right hand side, so it makes sense to also use it's type. This allows expressions like:
int foo;
num bar;
foo = bar = 1;
Commonly assignment operation returns the value that it assigns.
int a = 0;
print(a = 3);//Prints 3
So,
When stuff = null,
'stuff = null' returns null. if statement needs a boolean .null is a sub-Type of boolean.
if(null){}
is valid
When stuff = 42,
'stuff = 42' returns 42. if statement needs a boolean .42 is not a sub-Type of boolean.
if(42){}
is not valid

Resources