I got a warning on the following piece of code, and I don't know why.

List<Map<String, int>> callNoInfo = [];

int getCallNo(String phoneNo) {
  callNoInfo.forEach((item) {
    if (item.containsKey(phoneNo)) {
      return item[phoneNo];
    }
  });
  return 0;
}

The warning is:

This function has a return type of 'int', but doesn't end with a return statement. (missing_return at [project_name] lib\helper\call_no.dart:35)

Can anyone tell me why this happens? thanks in advance


Solution 1: Benjamin

You don't have an else case for the if statement inside the forEach loop. Even though you might have one at the end, it still is expecting a case everywhere.

List<Map<String, int>> callNoInfo = [];
int getCallNo(String phoneNo) {
  callNoInfo.forEach((item) {
    if (item.containsKey(phoneNo)) {
      return item[phoneNo];
    }
    // you don't have a case for here since it's in a callback
  });
  return 0;
}

However, you could do this which uses a for in loop:

List<Map<String, int>> callNoInfo = [];
int getCallNo(String phoneNo) {
  for (var item in callNoInfo) {
    if (item.containsKey(phoneNo)) {
      return item[phoneNo];
    }
  }
  return 0;
}


Solution 2: Abion47

In the forEach method, you are creating a lambda function without explicitly defining the return type, so Dart is attempting to infer it from the return statements. If we pull the function out of the forEach method, it might help to see what I mean:

...
(item) {
  if (item.containsKey(phoneNo)) {
    return item[phoneNo];
  }
}
...

The function includes a return statement that returns item[phoneNo], which is an int value. Using this, Dart infers that the return type of this lambda function is int. However, now that it knows this, it also notices that if the code execution does not enter the if block, there is no return statement to match the else side of the if condition. If the item object does not contain the key phoneNo, what is the method going to return?

(The answer is that the method will implicitly return null which is why the message is only a warning and not a compiler error, but the warning appears because this probably wasn't intentional by you the developer and also as a nudge to help you make your code less reliant on invisible Dart runtime magicks.)

To fix this, there needs to be another return outside of the if block:

...
(item) {
  if (item.containsKey(phoneNo)) {
    return item[phoneNo];
  }
  return 0;
}
...

However, now there's a different problem. The forEach method on lists has the following signature:

forEach(void f(E element)) → void

In fact, there are two problems. First, the method passed as the parameter needs to have a return type of void, and the forEach method itself also has a return type of void. This means that you cannot return values from within the forEach method at all.

The thing about the forEach method is that it is intended to iterate over the collection and process each of the values within it. It's not meant to (and can't) search for a value and return it once it's found. Furthermore, the iteration is exhaustive, meaning once you start it, the method cannot be stopped until each and every element in the collection has been iterated over.

Which is why, as the other answers have pointed out, what you really should be doing is using a for or for in loop:

List<Map<String, int>> callNoInfo = [];

int getCallNo(String phoneNo) {
  for(var item in callNoInfo) {
    if (item.containsKey(phoneNo)) {
      return item[phoneNo];
    }
  }
  return 0;
}

(I'm not sure why you don't get a compiler error for assigning a lambda function with a return value of int to the forEach method which clearly is requesting a one with a void return type. But if I had to guess, I'd say the Dart runtime treats them as compatible and reconciles the difference in return type by simply discarding the return value of the lambda function.)