Why does `if (var = null)` compile in dart? - 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

Related

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

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

Using an 'is' expression when the right-hand operand is a variable?

I am trying to write a function that takes two arguments: givenType and targetType. If these two arguments match, I want givenType to be returned, otherwise null.
For this objective, I am trying to utilize Dart's is expression (maybe there is a better way to go about it, I am open to suggestions). Initially, I thought it would be as simple as writing this:
matchesTarget(givenType, targetType) {
if (givenType is targetType) {
return givenType;
}
return null;
}
But this produces an error:
The name 'targetType' isn't a type and can't be used in an 'is'
expression. Try correcting the name to match an existing
type.dart(type_test_with_non_type)
I tried looking up what satisfies an is expression but cannot seem to find it in the documentation. It seems like it needs its right-hand operand to be known at compile-time (hoping this is wrong, but it does not seem like I can use a variable), but if so, how else can I achieve the desired effect?
I cant guess the purpose of the function (or the scenario where it would be used, so if you can clarify it would be great). First of all, I don't know if you are passing "types" as arguments. And yes, you need to specify in compile time the right hand argument of the is function.
Meanwhile, if you are passing types, with one change, you can check if the types passed to your function at runtime.
matchesTarget(Type givenType, Type targetType) {
print('${givenType.runtimeType} ${targetType.runtimeType}');
if (givenType == targetType) {
return givenType;
}
return null;
}
main(){
var a = int; //this is a Type
var b = String; //this is also a Type
print(matchesTarget(a,b)); //You are passing different Types, so it will return null
var c = int; //this is also a Type
print(matchesTarget(a,c)); //You are passing same Types, so it will return int
}
But if you are passing variables, the solution is pretty similar:
matchesTarget(givenVar, targetVar) {
print('${givenVar.runtimeType} ${targetVar.runtimeType}');
if (givenVar.runtimeType == targetVar.runtimeType) {
return givenVar.runtimeType;
}
return null;
}
main(){
var a = 10; //this is a variable (int)
var b = "hello"; //this is also a variable (String)
print(matchesTarget(a,b)); //this will return null
var c = 12; //this is also a variable (int)
print(matchesTarget(a,c)); //this will return int
}
The Final Answer
matchesTarget(givenVar, targetType) {
print('${givenVar.runtimeType} ${targetType}');
if (givenVar.runtimeType == targetType) {
return givenVar;
}
return null;
}
main(){
var a = 10; //this is a variable (int)
var b = String; //this is a type (String)
print(matchesTarget(a,b)); //this will return null because 'a' isnt a String
var c = int; //this is also a type (int)
print(matchesTarget(a,c)); //this will return the value of 'a' (10)
}
The as, is, and is! operators are handy for checking types at runtime.
The is operator in Dart can be only used for type checking and not checking if two values are equal.
The result of obj is T is true if obj implements the interface specified by T. For example, obj is Object is always true.
See the below code for an example of how to use the is operator
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
Even the error message that you're getting says that
The name 'targetType' isn't a type and can't be used in an 'is'
expression.
So the bottomline is that you can use is only for checking if a variable or value belongs to a particular data type.
For checking equality, you can use the == operator if comparing primitive types, or write your own method for comparing the values. Hope this helps!

A value of type 'T?' can't be assigned to a variable of type 'T'

I am trying to assign to an int value, however, I get this error:
A value of type 'int?' can't be assigned to a variable of type 'int'
The line causing this compile-time error is the following:
int number = someFunction();
The problem here is that the value returned from someFunction is nullable and trying to assign that value to a non-nullable variable is a compile-time error (null-safety was introduced in Dart 2.7).
You need to check for null using a != null condition.
Example
void exampleFunction<T>(T? input) {
T result = input; // <- ERROR here
print(result);
}
In this example, input is nullable and thus cannot be assigned to the non-nullable variable result.
Solution
The given example problem can be solved like this:
void exampleFunction<T>(T? input) {
if (input != null) {
T result = input;
print(result);
}
}

How to create nullable variable in Dart

I created a model class, this is one of my a variable in model class
Datetime hotel_effect_open_date
but JSON response hotel_effect_open_date is null, getting an error in my application. and I modified to DateTime to String, it's working. In API created in the .net core, it looks like this,
Nullable<DateTime> hotel_effect_open_date
How to create a nullable variable in DART language?
Now Dart is in the process of redesigning its type system. So that expression of that type can be either nullable or non-nullable. Something like this:
type? variable; // variable can be null.
Example:
int? a; // a can now be null.
Reference: Dart nullability syntax decision
TLDR:
int? aNullableInt = null;
Detailed explanation:
Dart 2... and above
documentation:
https://dart.dev/null-safety
With null safety, all of the variables in the following code are
non-nullable:
// In null-safe Dart, none of these can ever be null.
var i = 42; // Inferred to be an int.
String name = getFileName();
final b = Foo();
To indicate that a variable might have the value null, just add ? to
its type declaration:
int? aNullableInt = null;
Dart <2
DateTime example;
example = null;
Uninitialized variables have an initial value of null. Even variables with numeric types are initially null, because numbers—like everything else in Dart—are objects.
int lineCount;
assert(lineCount == null);

Why NULL needs typecasting with blocks?

See this scenario :
#property (nonatomic,copy) UIImage * (^bgImageBlock)(void);
Defination of bgImageBlock block variable :
objDrawing.bgImageBlock = ^(){
return (UIImage *)NULL;
};
bgImageBlock has return type UIImage that if I pass NULL like this :
objDrawing.bgImageBlock = ^(){
return NULL;
};
Would give compile time error : Incompitable block pointer type assigning to UIImage.
Whereas if I take simple UIImage variable and assign it NULL, It is absolutely fine. Then why in case of Blocks It can't accept NULL without typecast. ?
The compiler infers the block return type from the kind of object you return, if you don't explicitly tell it. So, in this case:
objDrawing.bgImageBlock = ^(){
return NULL;
};
...I assume it's seeing the NULL and inferring that the return type of your block is void *, the type of NULL. This doesn't match your property type, so the compiler complains. (Note that the error is about the "block pointer type"; it's a mis-match between the declaration of your block property and the block you're trying to store in it.)
If you explicitly tell it your type, you should be able to use your simple NULL return:
objDrawing.bgImageBlock = ^UIImage*(){
return NULL;
};
That the block type is inferred from the return type is documented in the Clang documentation:
The return type is optional and is inferred from the return statements. If the return statements return a value, they all must return a value of the same type. If there is no value returned the inferred type of the Block is void; otherwise it is the type of the return statement value.
This inference of the return type, and the fact that the compiler checks the pointer types for you, and produces the error you're getting if there's a mis-match, is mentioned in the 2013 WWDC session "Advances in Objective-C". (See the part titled "return type inference").
Your block is expecting the type UIImage*. However, NULL being a pointer, it just does not recognize it to be pointing to anywhere of the type UIImage* (be it nothing). Bam, error.
However, you are now casting a pointer. It does not actually do anything at runtime. It just modifies the type being pointed-to, which then affects subsequent operations on the resulting value. But the actual pointer value will remain the same through the cast.
While the actual pointer remains same (all though, the value being pointed to is nothing), it is now pointing to a type that the block is expected to be returning. All is well. :)
NULL is of type void *, so the block is inferred to have return type void *, and non-Objective-C-object pointer types (like void *) are not compatible with Objective-C-object pointer types (like UIImage *). Instead, you should use nil, which is also a null pointer, but of type id, which is compatible with all Objective-C-object pointer types.

Resources