for (var foo in bar) print(foo);
for (final foo in bar) print(foo);
Is there any difference in performance between these two? Or is the only difference "avoiding accidental reassignments"?
Edit:
prefer_final_locals mentions compiler performing optimizations when final is used. Will it apply here? dart2js output matches for the 2 snippets above. Not sure about dart2native.
The only difference is that you can reassign the foo value if you use var.
for (var foo in bar) {
foo = foo + 5;
print(foo);
}
doing so using final would not work
for (final foo in bar) {
foo = foo + 5; //Error: The variable foo can be set only once
print(foo);
}
From Dart Style Guide:
DON'T use final for local variables.
var is shorter, and final does not change the meaning of the code.
https://dart-lang.github.io/linter/lints/unnecessary_final.html
Related
Dart Language
What is the equivalent operator for the "as" operator in type checking during runtime?
var foo = new Teacher();
var bar = new Student();
like (foo as Teacher);
Output
Instance ofTteacher
The equivalent operator is "is"
(foo is Teacher) should return true or false accordingly if foo is of type Teacher or otherwise.
Key word as is used for type conversion.
If you want to check foo is Teacher or not:
print(foo is Teacher);
print(bar is Student);
If you just want to get Type of foo:
final fooType = foo.runtimeType;
final barType = bar.runtimeType;
print(fooType == barType);
Both fooType and barType is instance of Type.
Can I get strongly typed value from an Object, something like this:
Object obj;
final fooOrBar = obj as (flag ? Foo : Bar); // Error
fooOrBar should be either of type Foo or Bar but this gives me an error.
Note:
I don't want to do things like:
if (flag) {
final foo = obj as Foo;
} else {
final bar = obj as Bar;
}
You cannot do what you are asking for.
A variable has one type, determined at compile-time.
Since flag is not known at compile-time, it cannot affect the type of the variable fooOrBar.
You also cannot abstract over types like that. The thing after as must be a single type.
You can do
var fooOrBar = flag ? obj as Foo : obj as Bar;
but the static type of fooOrBar will likely be Object anyway, or at least some common supertype of Foo and Bar. Then you might as well just cast directly to that: var castObj = obj as CommonSupertypeOfFooAndBar;.
You can use helper functions:
Foo asFoo(Object o) => o as Foo;
Bar asBar(Object o) => o as Bar;
//...
var fooOrBar = (flag ? asFoo : asBar)(obj);
Again, the type won't be Foo or Bar, but some supertype of both.
If you actually care about the type of fooOrBar being either precisely Foo or precisely Bar, you need two different variables.
void main() {
var foo = number as int; // Works
for (var bar in numbers as List<int>) {} // Run-time error
}
num get number => 0;
List<num> get numbers => [0];
Note: I'm not looking for a solution how to make that work. The question is why I'm unable to downcast a List<num> to a List<int> when the list is actually a type of List<int>.
In this case, your list is not a List<int>.
The list value is generated by
List<num> get numbers => [0];
You didn't write a type on the list literal, like <int>[0], so the type of the list will be inferred for you.
Dart uses two pieces of information to infer that type:
The context type (what is needed by the context) of List<num>. Not all expressions have a context type, but this one does.
The element types (what's required by the elements) which is int.
If there is a context type, it always wins. So, your getter is inferred to be:
List<num> get numbers => <num>[0];
When you then try to do numbers as List<int>, it fails because a List<num> is-not a List<int> (the subtyping is the other way around).
For other people actually looking for a solution, you can either convert the list to a List<int>, by making a new list or by wrapping it using cast, or you can cast the individual elements. Or you can force an implicit down-cast. In all the cases, the code will fail if the list ends up containing a non-int value.
for (var bar in <int>[...numbers as List<dynamic>]) { ... }
for (var bar in numbers.cast<int>()) { ... }
for (int bar in numbers as List<dynamic>) { ... }
for (var bar in numbers) { ... bar as int ... }
class X extends Y {
X(int a, int b) : super(a,b);
}
Can someone give me an explanation about the syntax meaning of the colon :?
This feature in Dart is called "initializer list".
It allows you to initialize fields of your class, make assertions and call the super constructor.
This means that it is not the same as the constructor body. As I said, you can only initialize variables and only access static members. You cannot call any (non-static) methods.
The benefit is that you can also initialize final variables, which you cannot do in the constructor body. You also have access to all parameters that are passed to the constructor, which you do not have when initializing the parameters directly in the parentheses.
Additionally, you can use class fields on the left-hand of an assignment with the same name as a parameter on the right-hand side that refers to a parameter. Dart will automatically use the class field on the left-hand side.
Here is an example:
class X {
final int number;
X(number) : number = number ?? 0;
}
The code above assigns the parameter named number to the final field this.number if it is non-null and otherwise it assigns 0. This means that the left-hand number of the assignment actually refers to this.number. Now, you can even make an assertion that will never fail (and is redundant because of that, but I want to explain how everything works together):
class X {
final int number;
X(number): number = number ?? 0, assert(number != null);
}
Learn more.
It's ok to access non static member in initializer list.
class Point {
num x, y;
Point(this.x, this.y);
Point.origin(): this.x = 10, this.y = 10;
}
main() {
Point p = Point.origin();
print(p.x); // 10
}
After experimenting with Vala and inspecting the generated C source code i came up with the following Vala code:
class Foo : GLib.Object {
public string baz;
}
class Main : GLib.Object {
public static Foo foo;
public static void bar(Foo f) {
foo = null;
f.baz = "Hi";
}
public static int main(string[] args) {
foo = new Foo();
bar(foo);
return 0;
}
}
Inspecting the generated C code i realized that the Vala compiler didn't insert a reference count (RC) increment for foo when passing it to bar. So as far as i understand it, the first line in bar will decrement the RC of foo to 0, which in turn should free up foo, effectively making the passed variable f a dangling pointer, which is then accessed in the second line of bar. However, the program executes without problems, so i'm not sure whether i am missing something here or it works just by pure coincidence.
Here the generated C code for reference:
void main_bar (Foo* f) {
Foo* _tmp0_;
gchar* _tmp1_;
g_return_if_fail (f != NULL);
_g_object_unref0 (main_foo);
main_foo = NULL;
_tmp0_ = f;
_tmp1_ = g_strdup ("Hi");
_g_free0 (_tmp0_->baz);
_tmp0_->baz = _tmp1_;
}
gint main_main (gchar** args, int args_length1) {
gint result = 0;
Foo* _tmp0_;
Foo* _tmp1_;
_tmp0_ = foo_new ();
_g_object_unref0 (main_foo);
main_foo = _tmp0_;
_tmp1_ = main_foo;
main_bar (_tmp1_);
result = 0;
return result;
}
This is correct behaviour. Only owned references are counted. Parameters are unowned, unless explicitly specified otherwise. So f in bar, is never reference counted because the caller is responsible for maintaining the reference count. Variable storage locations (class fields, stack variables, global variables) are all owned.
So, let's examine main and bar separately:
main creates an instance of Foo, which needs to be put somewhere. It puts it in the global variable foo, which owns it. There is now a single reference on the object created via foo. We then call bar, which takes a parameter foo. We know that foo already references the object and that passing it as a parameter does not require us to increment the reference unless the parameter is owned. Therefore, we simply pass the pointer foo to bar.
bar takes a parameter of type Foo called f which is does not own. It assigns null to a completely unrelated global variable called foo, this decrements the object's reference count, cleaning it up as necessary. It then does an assignment to a field in f.
To have this work “correctly” the compiler would have to 1) understand that foo and f are the same, even though you can call bar with any parameter, 2) know that decrementing the reference count on foo is slightly different from decrementing it to zero in some cases. That's just much too complex for any compiler not capable of solving the halting problem.
To make your code work as expected you have two options:
Assign your new object both to the global variable foo and a stack variable which you pass to bar. You now have ensured that the variable remains live through the call to bar.
Make bar take owned Foo f instead of Foo f. This will cause the caller to increment the reference on foo before passing it and bar to decrement the reference when it's done.
In short, it's the caller's responsibility to make sure that a variable remains live when it passes it to a method for the life-span of that method. You can imagine that gets a little more complicated when that method is async.