I was looking for codes to understand the difference between final and const in dart. After I found some code blocks and changed a bit, the outputs were surprising for me. How to explain these outputs? What causes this difference?
void main() {
final list1 = [1, 2];
final list2 = [1, 2];
print(list1);
print(list2);
print(list1 == list2); //false
const list3 = [1, 2];
const list4 = [1, 2];
print(list3);
print(list4);
print(list3 == list4); //true
}
const means the object is created at compile time instead of when the program is running. Dart does also guarantee that if you create two const objects with the same arguments, it will both points to the same compile time object. This optimization is possible since const objects MUST be immutable and we can therefore safely just share the same instance multiple times.
The == operator will by default (and such also on List) check if two objects are the same as in the same physical object in memory.
Since your two const created lists are created with the same objects, the two lists variables, list3 and list4, will end up pointing to the same exact object created by the compiler and therefore list3 == list4 is going to be true.
Just exploring Zig... I have one .zig file with a bunch of comptime functions and constants, and I want to use those in other .zig programs. Equivalent to #include "my.h" in C.
The answer is that #import("foo.zig") almost does this. If you had foo.zig:
const a = 1;
pub const b = 2;
pub const c = 3;
then in main.zig:
const stdout = #import("std").io.getStdOut().writer();
const foo = #import("foo.zig");
const c = foo.c;
const a = foo.a;
test "#import" {
// try stdout.print("a={}\n",.{foo.a});
// try stdout.print("a={}\n",.{a});
try stdout.print("b={}\n",.{foo.b});
try stdout.print("c={}\n",.{c});
}
will print the values of b and c but if you uncomment the commented lines, you'll get an error because a wasn't exported (pub). Interestingly the const a=foo.a doesn't give an error unless a is used.
It appears that there is no way to dump all of an import into the current namespace, so if you want the names unqualified, you have to have a const line for each.
Thanks to people in the Zig discord, particularly #rimuspp and #kristoff
You actually can use:
const bar = struct {
usingnamespace #import("foo.zig");
};
to import a complete namespace into a struct, but not at the top level.
I'm currently using the pedantic package for my dart/flutter linting.
My analysis_options.yaml look like
include: package:pedantic/analysis_options.yaml
linter:
rules:
always_specify_types: true
omit_local_variable_types: false
I like having to specify types when I declare a variable line. But with this, I have to also specify the type on the created list/map.
The issues I have are the parts within **
const int number = 5;
const bool isTrue = false;
const String myName = 'Dan';
const List<int> numbers = **<int>**[1, 2, 3, 4];
const Map<String, int> ages = **<String, int>**{'Dan': 1};
It's redundent. How can I set my analysis_options.yaml to fix this.
Thanks!
I am getting an error on constant evaluation.
please take a look at this code:
class A {
final num a;
const A(this.a);
}
class B {
final A a;
const B(this.a);
}
main() {
const a = A(12);
const b = B(a); // this works fine
// I believe everything inside a const List is considered constant,
// please correct me if that is wrong
const aL = [ A(12), A(13) ]; // [ a, A(13) ] will not work either
const b2 = B(
aL[0], // here the error is happening
);
}
Error:
lib/logic/dartTest.dart:22:14: Error: Constant evaluation error:
const b2 = B(
^
lib/logic/dartTest.dart:23:7: Context: The method '[]' can't be invoked on '<A>[A {a: 12}, A {a: 13}]' in a constant expression. - 'A' is from 'package:t_angband/logic/dartTest.dart' ('lib/logic/dartTest.dart').
aL[0],
^
lib/logic/dartTest.dart:22:9: Context: While analyzing:
const b2 = B(
^
The list contains constant Object, then why the constant evaluation is failing? Shouldn't this be an analyzer issue? Am I missing something?
Thankyou.
Constant expressions can only build data, it cannot deconstruct it. You cannot call any methods on the constant objects except a handful of operations on numbers (and String.length, which also creates a number).
So, aL[0] is simply not a valid compile-time constant expression.
A possible fix may be to make b2 not constant!
I've noticed it's possible to create a const constructor in Dart. In the documentation, it says that const word is used to denote something a compile time constant.
I was wondering what happens when I use a const constructor to create an object. Is this like an immutable object which is always the same and available at compile time? How does the concept of const constructor actually work? How is a const constructor different from a regular constructor?
Const constructor creates a "canonicalized" instance.
That is, all constant expressions begin canonicalized, and later these "canonicalized" symbols are used to recognize equivalence of these constants.
Canonicalization:
A process for converting data that has more than one possible representation into a "standard" canonical representation. This can be done to compare different representations for equivalence, to count the number of distinct data structures, to improve the efficiency of various algorithms by eliminating repeated calculations, or to make it possible to impose a meaningful sorting order.
This means that const expressions like const Foo(1, 1) can represent any usable form that is useful for comparison in virtual machine.
The VM only needs to take into account the value type and arguments in the order in which they occur in this const expression. And, of course, they are reduced for optimization.
Constants with the same canonicalized values:
var foo1 = const Foo(1, 1); // #Foo#int#1#int#1
var foo2 = const Foo(1, 1); // #Foo#int#1#int#1
Constants with different canonicalized values (because signatures differ):
var foo3 = const Foo(1, 2); // $Foo$int$1$int$2
var foo4 = const Foo(1, 3); // $Foo$int$1$int$3
var baz1 = const Baz(const Foo(1, 1), "hello"); // $Baz$Foo$int$1$int$1$String$hello
var baz2 = const Baz(const Foo(1, 2), "hello"); // $Baz$Foo$int$1$int$2$String$hello
Constants are not recreated each time. They are canonicalized at compile time and stored in special lookup tables (where they are hashed by their canonical signatures) from which they are later reused.
P.S.
The form #Foo#int#1#int#1 used in these samples is only used for comparison purposes and it is not a real form of canonicalization (representation) in Dart VM;
But the real canonicalization form must be "standard" canonical representation.
I find Lasse's answer on Chris Storms blog a great explanation.
Dart Constant Constructors
I hope they don't mind that I copy the content.
This is a fine explanation of final fields, but it doesn't really
explain const constructors. Nothing in these examples actually use
that the constructors are const constructors. Any class can have final
fields, const constructors or not.
A field in Dart is really an anonymous storage location combined with
an automatically created getter and setter that reads and updates the
storage, and it can also be initialized in a constructor's initializer
list.
A final field is the same, just without the setter, so the only way to
set its value is in the constructor initializer list, and there is no
way to change the value after that - hence the "final".
The point of const constructors is not to initialize final fields, any
generative constructor can do that. The point is to create
compile-time constant values: Objects where the all field values are
known already at compile time, without executing any statements.
That puts some restrictions on the class and constructor. A const
constructor can't have a body (no statements executed!) and its class
must not have any non-final fields (the value we "know" at compile
time must not be able to change later). The initializer list must also
only initialize fields to other compile-time constants, so the
right-hand sides are limited to "compile-time constant
expressions"[1]. And it must be prefixed with "const" - otherwise you
just get a normal constructor that happens to satisfy those
requirements. That is perfectly fine, it's just not a const
constructor.
In order to use a const constructor to actually create a compile-time
constant object, you then replace "new" with "const" in a
"new"-expression. You can still use "new" with a const-constructor,
and it will still create an object, but it will just be a normal new
object, not a compile-time constant value. That is: A const
constructor can also be used as a normal constructor to create objects
at runtime, as well as creating compile-time constant objects at
compilation time.
So, as an example:
class Point {
static final Point ORIGIN = const Point(0, 0);
final int x;
final int y;
const Point(this.x, this.y);
Point.clone(Point other): x = other.x, y = other.y; //[2]
}
main() {
// Assign compile-time constant to p0.
Point p0 = Point.ORIGIN;
// Create new point using const constructor.
Point p1 = new Point(0, 0);
// Create new point using non-const constructor.
Point p2 = new Point.clone(p0);
// Assign (the same) compile-time constant to p3.
Point p3 = const Point(0, 0);
print(identical(p0, p1)); // false
print(identical(p0, p2)); // false
print(identical(p0, p3)); // true!
}
Compile-time constants are canonicalized. That means the no matter how
many times you write "const Point(0,0)", you only create one object.
That may be useful - but not as much as it would seem, since you can
just make a const variable to hold the value and use the variable
instead.
So, what are compile-time constants good for anyway?
They are useful for enums.
You can use compile-time constant values in switch cases.
They are used as annotations.
Compile-time constants used to be more important before Dart switched
to lazily initializing variables. Before that, you could only declare
an initialized global variable like "var x = foo;" if "foo" was a
compile-time constant. Without that requirement, most programs can be
written without using any const objects
So, short summary: Const constructors are just for creating
compile-time constant values.
/L
[1] Or really: "Potentially compile-time constant expressions"
because it may also refer to the constructor parameters.
[2] So yes, a class can have both const and non-const constructors at the same time.
This topic was also discussed in https://github.com/dart-lang/sdk/issues/36079 with some interesting comments.
Very well explained in detail but for the users who are actually looking for the usage of a const constructor
It is used to Increase Flutter Performance as it helps Flutter to
rebuild only widgets that should be updated.Means while Using
setState() in StateFulWidgets, only those components will be rebuild
that are non const constructor
Can be explained with example->
class _MyWidgetState extends State<MyWidget> {
String title = "Title";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: <Widget>[
const Text("Text 1"),
const Padding(
padding: const EdgeInsets.all(8.0),
child: const Text("Another Text widget"),
),
const Text("Text 3"),
],
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
setState(() => title = 'New Title');
},
),
);
}
}
As In this example, only Text title should get changed, so only this widget should get rebuild, so making all the others widgets as const constructor will help flutter to do the same for increasing performance.
in This video, You will know why we need it. https://www.youtube.com/watch?v=B1fIqdqwWw8&t=558s From 09:18 to 16:09
In Documentation: https://dart.dev/guides/language/language-tour
constant constructors. To create a compile-time constant using a
constant constructor, put the const keyword before the constructor
name:
> var p = const ImmutablePoint(2, 2);
Constructing two identical
compile-time constants results in a single, canonical instance:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1,1);
assert(identical(a, b)); // They are the same instance!
Within a
constant context, you can omit the const before a constructor or
literal. For example, look at this code, which creates a const map:
// Lots of const keywords here.
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
You can omit
all but the first use of the const keyword:
// Only one const, which establishes the constant context.
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)], };
If a constant
constructor is outside of a constant context and is invoked without
const, it creates a non-constant object:
> var a = const ImmutablePoint(1, 1); // Creates a constant
> var b = ImmutablePoint(1, 1); // Does NOT create a constant
>
> assert(!identical(a, b));// NOT the same instance!
For more information you can check these 2 Answers below:
1- https://stackoverflow.com/a/21746692/14409491
2- https://stackoverflow.com/a/21745617/14409491
An example demo that const instance really decide by final field.
And in this case, it cannot be predict in compile-time.
import 'dart:async';
class Foo {
final int i;
final int j = new DateTime.now().millisecond;
const Foo(i) : this.i = i ~/ 10;
toString() => "Foo($i, $j)";
}
void main() {
var f2 = const Foo(2);
var f3 = const Foo(3);
print("f2 == f3 : ${f2 == f3}"); // true
print("f2 : $f2"); // f2 : Foo(0, 598)
print("f3 : $f3"); // f3 : Foo(0, 598)
new Future.value().then((_) {
var f2i = const Foo(2);
print("f2 == f2i : ${f2 == f2i}"); // false
print("f2i : $f2i"); // f2i : Foo(0, 608)
});
}
Now dart will check it.
Dart Analysis:
[dart] Can't define the 'const' constructor because the field 'j' is initialized with a non-constant value
Runtime Error:
/main.dart': error: line 5 pos 17: expression is not a valid compile-time constant
final int j = new DateTime.now().millisecond;