A query about null safety

1,161 views
Skip to first unread message

Chris Norman

unread,
Mar 28, 2021, 1:22:18 PM3/28/21
to mi...@dartlang.org
Hi all,
I have a set of elements, defined as:

import 'dart:html';

final loadingDiv = document.querySelector('#loadingDiv');
final documentDiv = document.querySelector('documentDiv');
...

In another file, I want to ensure they're valid (I.E. I've typed the selectors correctly), then operate on them:

void main() {
  if (loadingDiv != null && documentDiv != null) {
    loadingDiv.hidden = true;
    documentDiv.hidden = false;
  }
}

When it comes to setting the .hidden properties, I get the error:

  error • js\main.dart:5:5 • The property 'hidden' can't be unconditionally accessed because the receiver can be 'null'. Try making the access conditional (using '?.') or adding a null check to the target ('!').
          • unchecked_use_of_nullable_value
  error • js\main.dart:6:5 • The property 'hidden' can't be unconditionally accessed because the receiver can be 'null'. Try making the access conditional (using '?.') or adding a null check to the target ('!').
          • unchecked_use_of_nullable_value

2 issues found.

I understand what the error is telling me, but surely if the definitions are final, they can't change value? I've read about how fields of a class need to be made local because the compiler can't guarantee that asynchronous code hasn't changed the initial values since your if statements, but final variables?

Thanks in advance.

Take care,

Chris Norman

Bob Nystrom

unread,
Mar 29, 2021, 12:45:43 PM3/29/21
to General Dart Discussion
Even final isn't enough for soundness, unfortunately:

class A {
  final bool? hidden;
  factory A() => SneakyA();
}

class SneakyA implements A {
  bool? get hidden => Random().nextBool() ? true : null;
}

In order to do sound field promotion, you need to know that the field is final and that it's not possible for there to exist any other classes that implement the class's implicit inferface. Currently, Dart doesn't have any way to express the latter. We're interested in it (for various reasons), but we don't have concrete plans or a design yet.

– bob

--
For more ways to connect visit https://dart.dev/community
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.
To view this discussion on the web visit https://groups.google.com/a/dartlang.org/d/msgid/misc/CANjhqb-M1QGDk0NmV9tQAD6%2BbfF8AQB9R-QtDYN3hy5s14BAww%40mail.gmail.com.

Lasse R.H. Nielsen

unread,
Apr 6, 2021, 5:56:58 AM4/6/21
to mi...@dartlang.org
In this case, it's not a field, just a top level variable, so technically `final` could be enough.

We've chosen to not promote any non-local variable anyway because it breaks variable/getter symmetry.
If we promote final variables (even top-level ones), but not getters, then it becomes a breaking change to change a field to a getter.
Since anyone with access to the field can potentially read it and promote it, it'd be a breaking change to change any final field to a getter. That goes completely against the idea of having getters to begin with, and encourages people to write their API as:

   final Foo _foo;
   Foo get foo => _foo;

just to not be locked into a field. This is the kind of unnecessary code that getters were supposed to avoid.

So, you can only promote local variables. 

/L



--
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84

Chris Norman

unread,
Apr 6, 2021, 10:37:47 AM4/6/21
to mi...@dartlang.org
"Well damn, thank you for that. I didn't realise you could override a final field with a getter.

I'm not sure if it's just me being daft, but I find that I don't fully understand Dart's class inheritance machinery.

Thank you for the reply, it was really helpful.

Take care,

Chris Norman



Alec Henninger

unread,
Apr 6, 2021, 10:37:56 AM4/6/21
to Dart Misc
Since the example was a final field of a *library* not a class, I'll add that library level fields work similarly to class level fields to the best of my knowledge.

The way I see it, a final field is a field without a setter, but callers aren't seeing whether it's a field or a getter, and so the getter can technically return something different the next time it's called since it's otherwise just a function, just like Bob's SneakyA example getter.

Tom Yeh

unread,
Apr 6, 2021, 10:21:54 PM4/6/21
to Dart Misc, Bob Nystrom
This field-promote issue is one of annoying issues to deal with when migrating to null safety. Worse of all, in 99.9% of code, a field's value won't be changed unless the setter is called. It is a boring and error-prone process repeated over and over! Programming shall be a joy.

As discussed in https://github.com/dart-lang/sdk/issues/35525, I'd like to suggest to have a special keyword for fields that can be changed before setter is called. For example,

volatile bool? get hidden => Random().nextBool() ? true: null;

Another question: Will this field-promote issue cause the generate code less efficient? That is, will it prevent from caching field's value in registers? Or, it has to retrieve the value each time even if there is no getter defined at all?

Erik Ernst

unread,
Apr 7, 2021, 4:16:58 AM4/7/21
to Dart Misc, Bob Nystrom
On Wed, Apr 7, 2021 at 4:21 AM Tom Yeh <tom...@potix.com> wrote:
This field-promote issue is one of annoying issues to deal with when migrating to null safety. Worse of all, in 99.9% of code, a field's value won't be changed unless the setter is called. It is a boring and error-prone process repeated over and over! Programming shall be a joy.

The proposal about stable getters may be helpful here: If a variable is declared as `stable` rather than `final` then it's part of the public contract of the associated getter that it will return the same value each time it's invoked.

It's a breaking change to replace a top-level or `static` variable declared `stable` by an ordinary getter, but it can be replaced by a `stable` getter (where the same property is enforced). This makes it possible for developers to commit to that contract: "I promise that this variable/getter will return the same result every time". It seems useful to me that this is possible, and in this case it's not a valid complaint that "we cannot allow it to be a breaking change to change a final variable to an ordinary getter".

This would make a large subset of those 99.9% of non-local variables officially stable. This takes away a certain amount of flexibility, but if stability is what we want then why shouldn't we be able to declare it?



--
Erik Ernst  -  Google Danmark ApS
Skt Petri Passage 5, 2 sal, 1165 København K, Denmark
CVR no. 28866984

Reply all
Reply to author
Forward
0 new messages