How does the const constructor actually work?

DartConstructor

Dart Problem Overview


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?

Dart Solutions


Solution 1 - Dart

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.

Solution 2 - Dart

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.

Solution 3 - Dart

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.

Solution 4 - Dart

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

Solution 5 - Dart

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;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmarkovuksanovicView Question on Stackoverflow
Solution 1 - DartmezoniView Answer on Stackoverflow
Solution 2 - DartGünter ZöchbauerView Answer on Stackoverflow
Solution 3 - DartB.shrutiView Answer on Stackoverflow
Solution 4 - DartAdel DanielView Answer on Stackoverflow
Solution 5 - DartTicore ShihView Answer on Stackoverflow