Open xvrh opened 4 years ago
Actually, some have suggested the !! operator for just that.
The !!
operator does not force you to do anything which is not null safe since there is no checked exceptions. In this context I'd much prefer the return type to be nullable. It's indeed a bit annoying when you are sure that there is an element in the list, but if you are really sure I don't see why !
would not fit here.
I would be backward to have to add !!
when you are sure than the where
clause might not be matched all the time instead of adding !
when you are sure it will.
The one issue with just making the return type nullable is that you can no longer distinguish a null from finding no value satisfying where, and where null is a value which satisfies the where.
That's reasonable but that's such an edge case compared to the dozen times you are not in this scenario does it matter ?
It's been a while, but I still think it would be beneficial to make firstWhere
nullable (note the lack of shouldThrow
):
E? firstWhere(bool Function(E) test, {@deprecated E Function()? orElse}) {
for (final E element in this) {
if (test(element)) return element;
}
if (orElse != null) return orElse();
return null;
}
To avoid breaking changes, keep the current semantics of orElse
, but mark it @deprecated
and encourage ??
instead (although removing it may actually be safe, see below).
I thought a bit more, and I don't think we need to handle a case where firstWhere
checks for null. If you want to check that null
is in your list, use .contains(null)
. But if you want the element that equals null
... just use null
? I'm not sure why we had so many examples in this thread like
List<int?> a;
int? b = myList.firstWhere((c) => c == null);
There is no benefit to checking myList
here, just set b = null
directly. Thus, using ??
for defaults is never an issue.
Then, the big breaking change that doesn't get caught automatically is people using try/catch
(with something useful in the catch
) and depending on .firstWhere
to throw, like this:
List<int> a;
int? b;
// this won't be affected by making firstWhere nullable, as b will still be null
try { b = a.firstWhere((c) => c % 2 == 0); } catch (_) { /* leave b null */}
// but this will, as the catch block will never run
try { b = a.firstWhere((c) => c % 2 == 0); } catch (_) { b = 0; }
But I don't think that's such a big issue either. In the first case, as the comment says, making .firstWhere
return null
on failures keeps the current behavior. In the case of a useful default, people are hopefully using orElse
instead, which is the only case that would really break:
int b = list.firstwhere((c) => c % 2, orElse: () => 0); // int? cannot be assigned to int
However, this is a relatively quick fix by either changing orElse
to ??
or adding a !
, which can be automated with a dart fix
.
Would be nice. If I had to redesign firstWhere
today, I'd definitely do that. (And, shucks, someone actually told me that orElse
was a bad idea when I first introduced it, so ... you were right! We should have designed with null safety in mind all the way :cry:.)
It's still not going to happen.
There is too much code out there which will need to be changed from .firstWhere(...)
to .firstWhere(...)!
. It's just not viable to migrate it all.
We can't just rely on dartfix because a change to the API will affect all code, not just code migrated to the newest language version.
We'll need some kind of API versioning for us to be able to make such a breaking change work. We don't have that.
Also, all the existing third-party implementations of firstWhere
which return a non-nullable result will stay valid, and will still throw. We need to migrate those too, but there is nothing which will help us find them (since they're still valid).
Since Dart 3.0.0 is about streamlining the dart APIs, enabling null safety by default and contains a lot of breaking changes anyways, couldn't this please be fixed/redesigned too now?
I'd love to, but it's still really not a viable breaking change. Too hard and work-intensive to migrate, too breaking to not migrate.
I don't expect this change can happen until we actually get library versioning, where new code can use the new API, and old code can stay on the old API, on the same object and type. That's not currently on the table (we keep thinking about it, but it's not on the short-list of features we are currently working actively on.)
Until that happens, firstWhereOrNull
from collection is OK
I will note that in practice firstWhere
is becoming a bit of a footgun. If we could get firstWhereOrNull
in the core framework it would be more discoverable
My workaround is to wrap the firstWhere
logic with try catch block, and handle null exceptions inside catch
Maybe there should be a @deprecated
on .firstWhere()
that recommends to use firstWhereOrNull
? Or if @deprecated
would cause too many warnings, a lint in package:lints/recommended.yaml
? If that's too breaking, a lint that's not enabled by default but is recommended on the firstWhere
docs so teams can audit their firstWhere
use?
The reason I bring it up is because !
is the recommended way of exposing these kinds of errors, but firstWhere
hides it when quickly reading code.
EDIT - best solutions so far:
package:collection
Original question:
I want to search a list and return null when the element is not found
https://nullsafety.dartpad.dev/2d0bc36ec1f3a5ade5550c0944702d73
With null safety, I get this error:
The
orElse
parameter offirstWhere
is declared not nullable:Why not?
What is the recommended way to search a collection with null safety enabled?