What has the difference between declaring a variable with var and int do with the Null Safety? - dart

Why does Dart allow this:
void main() {
var A;
A ??= 12;
print(A);
}
The output is 12. But not this:
void main() {
int A;
A ??= 12;
print(A);
}
Here's the error:
lib/project_dart.dart:4:2: Warning: Operand of null-aware operation '??=' has type 'int' which excludes null.
A??= 12;
^
lib/project_dart.dart:4:2: Error: Non-nullable variable 'A' must be assigned before it can be used.
A??= 12;
^
lib/project_dart.dart:5:8: Error: Non-nullable variable 'A' must be assigned before it can be used.
print(A);
^
In this case I have to add the ? after int so it can work but in the previous case it works fine without it the question is WHY?

var tells the compiler "I want you to figure out what type this variable should be" based on the assignment. In this case, there is no assignment to the variable upon declaration, so there is no information for the compiler to use to infer the type. Thus, according to the compiler, the type of A should be dynamic, which could be literally anything, so it's given the default value of null.
int explicitly tells the compiler "I want this variable to be a non-nullable int". Non-nullable variables cannot be given a default value when they are declared without a value, so they must be assigned before they are referenced for the first time, which means you can't do print(A) before A has been definitely given a value.
A ??= 5; is less obvious why it's failing, but think about what it's doing. It checks if A is null, and if it isn't, it assigns it the value of 5. In order to check A, the program needs to reference A, and as we know, A can't be referenced because it's not nullable and hasn't been definitely assigned a value. Additionally, A ??= 5 is redundant because A is non-nullable and therefore can never be null.
Note that a declared variable that hasn't been assigned a value is not going to implicitly contain null. There is no check for whether a variable has been assigned yet, which is why you should be careful when declaring non-nullable variables without immediately assigning them a value. It's easy to find yourself in a race condition where code that will assign the variable may or may not occur before other code that will reference the variable.

Related

What is the most up to date IsDefined operator in Rascal?

According to Rascal's documentation, the "?" operator can be used to query if a variable is "defined".
For example:
int u=1;
int v; // Defined but uninitialised
u = v?2;
v is uninitialised and therefore u will get the value 2.
However, doing this flags a "Warning: deprecated feature: run-time check on variable initialisation"
Hence the question, what is the non-deprecated way to do what the ? operator did in Rascal?
You can check with the IsDefined operator only things that can in principle be "undefined". Variables are not in that class; they were accidentally and now we are deprecating that behavior. In principle, there exists no null or undefined value in Rascal.
Having said that there are situations with maps and keyword fields of nodes and algebraic constructors where it is possible that a declared name does not exist at runtime. So:
myMap[myKey]?def; // a map does not have to have the key
myCons.myKeywordField?def ; // a keyword field does not have to be set
The isDefined operator is part of the assignment syntax on the left-hand side, as explained here: https://www.rascal-mpl.org/docs/Rascal/Statements/Assignment/IsDefined/
Also, the same syntax can be used as an expression: https://www.rascal-mpl.org/docs/Rascal/Expressions/Values/Boolean/IfDefinedElse/
Again, checking variables for undefinedness does not make sense since variables are always defined in Rascal. It is a static error otherwise. Defined but uninitialized variables are for making matching patterns look more elegant:
int i; int j; // here they are declared with a type
// here they are not defined and may not be used
if (<i, j> := <1,2>) { // here they are bound/defined
// here they can be used
}
// here i and j are not defined again and may not be used

Why dart type casting affects the original casted variable

I am learning dart programming language version 2.14.3 and came across an odd issue that I am unable to understand.
The following code doesn't compile obviously because isEven is only defined in int class.
void main() {
const num someNumber = 3;
print(someNumber.isEven);
}
However, casting someNumber to int and assigning the value to a different variable, solved the problem and the code compiles with print(someNumber.isEven); not changed. The following code compiles.
void main() {
const num someNumber = 3;
final someInt = someNumber as int;
print(someNumber.isEven);
}
Is this a bug in dart or a language feature that I am not aware of?
When you do:
final someInt = someNumber as int;
print(someNumber.isEven);
If the cast fails, an uncaught TypeError will be thrown and exit your function. Therefore, the compiler can logically deduce that, if print(someNumber.isEven) is reached, someNumber must be an int. Therefore, on lines that follow the cast, the original object is promoted to the casted type. (Note that only local variables can be promoted.) Put another way, the cast acts as a type assertion; you don't need the someInt variable:
someNumber as int;
print(someNumber.isEven);
This is the same principle by which ! (which is basically a non-null cast operator) can promote a local nullable variable to be non-nullable and avoid the need to pepper ! for subsequent accesses to the same variable.

Why can't I initialize a "const" variable with a primitive value received as a function argument in Dart?

I understand that, in Dart, primitives are passed by value and Objects are passed by reference.
So, I expected that
void test(String phrase) {
const _phrase = phrase;
}
would result in error, but
void test(int amount) {
const _amount = amount;
}
wouldn't.
However, both of them throws the same compile-time error: Const variables must be initialized with a constant value.
Is this some not implemented feature or there's a reason behind not accepting function arguments in const variables initialization?
Dart constant variables must be initialized with compile-time constant expressions.
A compile-time constant expression must always have the same value—precisely one value per source location.
Dart doesn't have "constant values" as such. It has constant expressions, which are known to evaluate to precisely one value, and for which it's possible to know this value at compile-time. That allows the compiler to canonicalize those constants values, so different constant expressions evaluating to constant objects with the same state are canonicalized to be the same object.
Your amount variable is not a compile-time constant expression. It can have different values at different times (because it's a function parameter and people might call the function with different arguments), so it cannot be a constant expression.
And therefore it cannot be used to initialize a constant variable, because constant variables can only have one value.
void test(int amount) {
const _amount = amount;
const list = [_amount]; // <- MUST ALWAYS HAVE SAME VALUE
}
In short: Dart constant variables must be initialized with a compile-time constant expression. A constant expression must always have the same value. This is the fundamental rule about Dart constant expressions which most other restrictions are derived from. (For example, a constant variable being used is a constant expression, so it must always be bound to the same value, which is why it must be initialized with a constant expression.)
I believe the answer is that constants are fixed at compile time rather than run time so function arguments cannot be passed to them as the function only gets executed at runtime.
See also here for example https://stackoverflow.com/a/58877374
A final modifier instead of const would work however.
By the way a String is also a primitive I think. See here https://stackoverflow.com/a/58568542

Why TweenAnimationBuilder is not able to infer the type using its tween property?

TweenAnimationBuilder(
tween: Tween<int>(begin: 0, end: 1),
builder: (_, value, __) {
return Container();
},
)
I'm providing a Tween<int> to tween, and according to my understanding, the value parameter in the builder should be of type int (at runtime it is definitely of type int but not at the compile time). Why is that so?
On the contrary, in the following code:
List<T> _getNewList<T>(List<T> list) => List<T>.from(list);
This code can infer its type from the arguments supplied.
final foo = _getNewList(<int>[1, 2]); // foo is of type `List<int>`
The way Dart type inference works is that a missing type argument, like the <T> type parameter on TweenAnimationBuilder is first checked against the calling context. If that doesn't give a type, then the arguments are analyzed individually without any hint to the type variable's type, and then the type argument is inferred as something allowing the argument's types.
In this case, tween is analyzed to have type Tween<int> and builder is analyzed to have type Widget Function(BuildContext, dynamic, Widget) because there is no clue to the second argument's type from the context.
The type analysis does not use information derived from one argument to infer T before finding the type of the second argument (that could be wrong if case the second one requires a looser type).
Then the type inference tries to find a type argument to TweenAnimationBuilder satisfying both of these, which ends up with <dynamic> because of the builder type.
If you only have one argument, or only one argument depending on the type argument, or you have multiple arguments that agree on the type, then things work out.
If you have multiple parameters depending on the type variable, no context type which locks the type variable down, and only one of the arguments actually constrain the type argument, then ... well, this happens, the other argument is unconstrained and that makes the inferred type argument be dynamic. Either write TweenAnimationBuilder<int>(...) or ..., builder: (_, int value, _) => ... so that you have a type hint on all paths.

How does Objective-C initialize C struct as property?

Consider below struct:
typedef struct _Index {
NSInteger category;
NSInteger item;
} Index;
If I use this struct as a property:
#property (nonatomic, assign) Index aIndex;
When I access it without any initialization right after a view controller alloc init, LLDB print it as:
(lldb) po vc.aIndex
(category = 0, item = 0)
(lldb) po &_aIndex
0x000000014e2bcf70
I am a little confused, the struct already has valid memory address, even before I want to allocate one. Does Objective-C initialize struct automatically? If it is a NSObject, I have to do alloc init to get a valid object, but for C struct, I get a valid struct even before I tried to initialize it.
Could somebody explains, and is it ok like this, not manually initializing it?
To answer the subquestion, why you cannot assign to a structure component returned from a getter:
(As a motivation this is, because I have read this Q several times.)
A. This has nothing to do with Cbjective-C. It is a behavior stated in the C standard. You can check it for simple C code:
NSMakeSize( 1.0, 2.0 ).width = 3.0; // Error
B. No, it is not an improvement of the compiler. If it would be so, a warning would be the result, not an error. A compiler developer does not have the liberty to decide what an error is. (There are some cases, in which they have the liberty, but this are explicitly mentioned.)
C. The reason for this error is quite easy:
An assignment to the expression
NSMakeSize( 1.0, 2.0 ).width
would be legal, if that expression is a l-value. A . operator's result is an l-value, if the structure is an l-value:
A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member,82) and is an lvalue if the first expression is an lvalue.
ISO/IEC 9899:TC3, 6.5.2.3
Therefore it would be assignable, if the expression
NSMakeSize( 1.0, 2.0 )
is an l-value. It is not. The reason is a little bit more complex. To understand that you have to know the links between ., -> and &:
In contrast to ., -> always is an l-value.
A postfix expression followed by the -> operator and an identifier designates a member of a structure or union object. The value is that of the named member of the object to which the first expression points, and is an lvalue. 83)
Therefore - that is what footnote 83 explains – ->, &, and . has a link:
If you can calculate the address of a structure S having a component C with the & operator, the expression (&S)->C is equivalent to S.C. This requires that you can calculate the address of S. But you can never do that with a return value, even it is a simple integer …
int f(void)
{
return 1;
}
f()=5; // Error
… or a pointer …
int *f(void)
{
return NULL;
}
f()=NULL; // Error
You always get the same error: It is not assignable. Because it is a r-value. This is obvious, because it is not clear,
a) whether the way the compiler returns a value, esp. whether he does it in address space.
b) when the time the life time of the returned value is over
Going back to the structure that means that the return value is a r-value. Therefore the result of the . operator on that is a r-value. You are not allowed to assign a value to a r-value.
D. The solution
There is a solution to assign to a "returned structure". One might decide, whether it is good or not. Since -> always is an l-value, you can return a pointer to the structure. Dereferencing this pointer with the -> operator has always an l-value as result, so you can assign a value to it:
// obj.aIndex returns a pointer
obj.aIndex->category = 1;
You do not need #public for that. (What really is a bad idea.)
The semantics of the property are to copy the struct, so it doesn't need to be allocated and initialized like an Objective-C object would. It's given its own space like a primitive type is.
You will need to be careful updating it, as this won't work:
obj.aIndex.category = 1;
Instead you will need to do this:
Index index = obj.aIndex;
index.category = 1;
obj.aIndex = index;
This is because the property getter will return a copy of the struct and not a reference to it (the first snippet is like the second snippet, without the last line that assigns the copy back to the object).
So you might be better off making it a first class object, depending on how it will be used.

Resources