Reputation: 167
hello when i use this generic method i get this error
A value of type 'num' can't be assigned to a variable of type 'T'. Try changing the type of the variable, or casting the right-hand type to 'T'.
what the error here
sums<T extends num>(List<T> list) {
T res =list[0];
for (var i = 1; i < list.length; i++) {
res = res + list[i];
}
print(res);
}
Upvotes: 5
Views: 23004
Reputation: 24671
This appears to either be a bug or a misunderstood feature of Dart 2.12+. In either case, it might be worth opening an issue on the Dart Github page.
If I run the following code in 2.12.0:
void main() {
final nums = [1, 2.0, 3.5];
final ints = [1, 2, 3];
final doubles = [1.1, 2.2, 3.3];
sums(nums);
sums(ints);
sums(doubles);
}
void sums<T extends num>(List<T> list) {
T res = list[0];
for (var i = 1; i < list.length; i++) {
res = res + list[i];
}
print(res);
}
I get the error you mentioned:
main.dart:14:9: Error: A value of type 'num' can't be assigned to a variable of type 'T'.
res = res + list[i];
^
If, however, I run the same code in Dart 2.10.5, the program compiles and runs without a problem and I get the following printout:
6.5
6
6.6
My best guess is that there is an issue with type inference where the type parameter of the List
is concerned. Instead of taking the actual type parameter passed to sums
, it seems instead to be taking the type restraint num
as its inferred type. As a result, the type system sees that you are adding a num
to a T extends num
and then assigning it to the latter. (To explain, imagine T
is int
. A num
plus an int
would result in a num
. Trying to then assign that to an int
results in a type error since, for all the type system knows, the num
might actually be a double
.)
At any rate, the error goes away once you do what the error suggests and cast the elements of list
to T
:
void sums<T extends num>(List<T> list) {
T res = list[0] as T;
for (var i = 1; i < list.length; i++) {
res = res + list[i] as T;
}
print(res);
}
// Prints:
//
// 6.5
// 6
// 6.6
Interestingly, though, using an addition-assignment instead will cause the same error regardless of the cast:
void sums<T extends num>(List<T> list) {
T res = list[0];
for (var i = 1; i < list.length; i++) {
res += list[i] as T;
}
print(res);
}
// Prints:
//
// main.dart:14:9: Error: A value of type 'num' can't be assigned to a variable of type 'T'.
// res += list[i];
// ^
EDIT: I've filed an issue for this topic on the Dart SDK Github page.
EDIT 2: Turns out this is not a bug but the result of an intended change to Dart as part of 2.12. See Irn's answer for details.
Upvotes: 3
Reputation: 71693
The issue here is that Dart's new Null Safety feature, introduced in Dart 2.12.0, removed implicit downcasts from the language.
If res
has type T
where T extends num
, then the operation res + something
has static type num. The only thing we know about T
is that it implements num
, and num.operator+
returns num
.
However, num
is not assignable to T extends num
because the latter is a subtype of the former (T
could be int
, not all num
values are assignable to int
).
So, you need an explicit cast to make the assignment valid:
res = (res + list[i]) as T;
is such a cast. If you just write res = res + list[i] as T;
, the precedence of as
means that it's the same as the cast above.
In Dart 2.10.x, it worked without the explicit cast because the language inserted an implicit cast for you.
If you write res += list[i] as T;
it instead means res = res + (list[i] as T);
which is an unnecessary cast since list[i]
already has type T
, and doesn't cast the result of the addition.
The reason this doesn't fail all the time for int res = ...; res = res + otherInt;
is that the language specification treats int.operator+
specially (along with some similar integer operators) and recognizes when the result is an integer, even if the +
operator's return type is num
. That special-casing does not apply to T extends num
.
Upvotes: 16