Bang operator vs assert statements

Issue

In the code below, I’m casting away the nullability by using the bang operator.

void main() {
  String? foo;
  foo!.toLowerCase();
}

But when I use assert just before calling the function, I get an error.

void main() {
  String? foo;
  assert(foo != null);
  foo.toLowerCase(); // Error
}

Is bang operator not doing the same thing behind the scene?

Note: I’m not looking for a solution to how to make it work, there are many ways, even the flow statement ones, if (foo == null) return;

Solution

(Adding this an answer instead of a comment.)

The point of asserts is that they can be disabled and that they will not incur any runtime penalty. It’s been some matter of debate, but the current philosophy is that this means that asserts will not perform type promotion, so even if you do:

bool foo(Object object) {
  assert(object is String);

  // error: The getter 'isEmpty' isn't defined for the type 'Object' 
  return object.isEmpty;
}

For the same reason, assert(someLocalVariable != null) will not promote someLocalVariable to a non-nullable type:

bool bar(String? string) {
  assert(string != null);

  // error: The property 'isEmpty' can't be unconditionally accessed
  //        because the receiver can be null.
  return string.isEmpty;
}

As of Dart 2.12 with null-safety enabled, however, you can get the desired effect by just performing the cast directly. That will promote the type and throw a runtime exception if the cast fails:

bool foo(Object object) {
  object as String;
  return object.isEmpty;
}

bool bar(String? string) {
  string!;
  return string.isEmpty;
}

Answered By – jamesdlin

Answer Checked By – Mary Flores (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *