Dart: var
vs final
When you declare a local variable, assuming you are using
omit_local_variable_types
as I recommend elsewhere, you have a choice between declaring it
var
or
final
.
final x = 3;
var y = 4;
The
final
keyword means that
x
will never receive a different value; the
var
keyword means that
y
might receive a different value.
Because we’re talking about local variables, there is no mystery: you can tell locally, quickly and for sure whether there is another statement assigning to
y
, or whether it is effectively
final
.
Oft discussed, rarely decided
Over the years I’ve spent a
lot of time discussing this one.
Very good software developers have argued both sides, convincingly. In fact, I’ve been discussing this issue for so long that
I have argued both sides.
Initially I argued for “prefer final”, and many of us did follow that style. You can see it for example in my early open source Dart code.
🔗 Initial built_value
commit in 2015 following “prefer final”
I didn’t convince enough people to make it happen.
Many years later I was persuaded by the “var” camp, and in 2019 I went so far as to contribute a lint for “prefer var”.
🔗 Add new lint: unnecessary_final
I enthusiastically argued that we should standardize on “prefer var”.
But, again, I didn’t convince enough people to make it happen.
When an issue goes for the best part of a decade with people arguing both sides and no resolution, it starts to be a good bet that
both options are good. Or bad; but in this case, if you like Dart, and you know I do, it must be that they’re both good.
Consistency wins
So is that the end of it? Just leave the matter open?
Not quite. Because it’s valuable to simply
pick one.
If you pick and enforce “prefer final”, you gain the advantage that the
only use of
var
is exactly when a variable
has multiple assignments in the local scope. Both
var
and
final
become more meaningful than they were before, and you always know which one to use. It’s strictly better than leaving the choice open.
If you pick and enforce “prefer var”, you get the advantage of fewer characters: it’s a little bit quicker to read, statements are a bit more likely to fit without wrapping. And again, you always know which one to use. It’s
also strictly better than leaving the choice open.
But you don’t have to take my word for it—
Effective Dart says exactly this.
🔗 DO follow a consistent rule for var
or final
on local variables
So we only have one problem: which of two equally good options to pick?
So, really: var
or final
?
For years I led the work at Google on picking lints for internal use, so in the end—after many, many years of discussion—I was the one who made the call for all of Google’s (internal) Dart code.
At the time I was in favour of
var
, but had given up on convincing enough people to reach a consensus. Then one day I decided to look at the data.
I classified all Google’s (internal) Dart source files as one of three things: “prefers var”, “prefers final”, or “inconsistent”. Then I looked at the distribution of each type of file.
And what I found, to my mild dismay due to my personal preference at the time, was overwhelming evidence for
one of the choices.
- The biggest group of packages/codebases/teams was in the “prefer final” category, by which I mean: there were many large islands of code where “prefer final” had been enforced, purely by choice of the owning team.
- The second biggest group of packages/codebases/teams was in the “inconsistent” category: individual inconsistent files, or files following different rules in the same package.
- There were a very small number of packages/codebases/teams in the “prefer var” category.
And so having gone to the trouble of gathering the data, I saw no choice but to follow it and reverse my opinion—in favour of consistency and towards “prefer final”.
We updated all the inconsistent code and
almost all the “prefer var” code to the “prefer final” style. I gave teams the option of sticking with “prefer var” if they wanted to—but, in fact, only one team did. Most very graciously agreed to switch styles for the sake of consistency with the majority.
There’s no
particular reason for you to want to be consistent with Google’s internal Dart code, of course. But I do hope you also manage to pick one of the two for your codebase, then enforce it—then forget about it.
- “prefer final”:
prefer_final_locals
and prefer_final_in_for_each
- or for “prefer var”:
unnecessary_final
Deep immutability
One last note to wrap up.
An argument I’ve heard a lot for “prefer final” is that it
reduces errors, but I think that’s largely not true. Rather, I think the feature people really want in this space—the feature that works—is
deep immutability.
Which you can get—but not using
final
. Currently you’ll need an immutable collections package, such as
built_collection
, and some immutable classes codegen, such as
built_value
or
freezed
.
📄 Dart
📄 Dart:
var
vs
final