dart-code-metrics
dart-code-metrics copied to clipboard
[New rule] Avoid forming and after that returning lists in loops
Avoid forming and after that returning lists in loops. Because double memory consumption with more reduant code.
With iterable result you can transform it to Stream For example, to avoid affecting the interface and event loops: https://dartpad.dev/6fad9242381bf89af4c1f6110c429e1c?null_safety=true
With iterable result you can much more effective with .take(n).toList() or .first or .isEmpty or .map<T>(...).toList() afterwards.
This is a very critical moment for collections displayed in lists, code generation, static analysis, parsing, reading files, converting collections.
Codegeneration, parsing, transforming must operate with Iterable not List, it's faster, lazy, less RAM consumption.
[X] Suggests an alternate way of doing something (suggestion)
Example:
BAD:
List<int> bad() {
final list = <int>[];
for (var i = 0; i < 1000; i++) {
list.add(i);
}
return list;
}
GOOD:
Iterable<int> good() sync* {
for (var i = 0; i < 1000; i++) {
yield i;
}
}
GOOD:
Iterable<int> good() => Iterable<int>.generate(
1000,
(i) => i,
);
As an example of incorrect code from third party developers

As an example of correct code from SDK

You can use Iterable something like this to avoid impact on Event loop and User Interface
!!! THIS IS NOT TESTED AND PRODUCTION READY CODE !!!
Stream<T> fixEventLoop<T extends Object?>(Iterable<T> iterable) {
final stopwatch = Stopwatch();
StreamSubscription<T>? sub;
final sc = StreamController<T>(
onListen: stopwatch.start,
onPause: () => sub?.pause(),
onResume: () => sub?.resume(),
onCancel: () {
sub?.cancel();
stopwatch.stop();
},
);
sub = Stream<T>.fromIterable(iterable).asyncMap((value) async {
if (stopwatch.elapsedMilliseconds > 8) {
await Future<void>.delayed(Duration.zero);
stopwatch.reset();
}
return value;
}).listen(
sc.add,
onDone: sc.close,
onError: sc.addError,
cancelOnError: true,
);
return sc.stream;
}
But with List it's doesn't make sense. Because in the process of forming the list, you ALREADY hung up the interface, since you bypassed the loop completely.
That's an interesting problem, but I think that the size of a list is a key issue here, if we'll implement such rule - because for small lists, like from 5 to 100 items, it doesn't look like a needed optimization and looks more like a false-positive to me. What do think? Maybe you have some measurements when the size of the list really starts matter?
@incendial if you need download and save some picture. Of couse you can download binary data in Uint8List in RAM and after that save it as File.
But it would still be correct to connect the download stream with IOSink.
So it is here. I thought you were doing these rules in order to write the correct code, and not protect the wrong one)
In addition, the "correct" option is shorter, beautiful, and more practical)