unsafe_ variance
Learn about the unsafe_variance linter rule.
Unsafe type: Has a type variable in a non-covariant position.
Details
#
An instance variable whose type contains a type parameter of
the enclosing class, mixin, or enum in a non-covariant
position is likely to cause run-time failures due to failing
type checks. For example, in
class C<X> {...}, an instance variable of
the form void Function(X) myVariable; may cause
this kind of run-time failure.
The same is true for a getter or method whose return type has a non-covariant occurrence of a type parameter of the enclosing declaration.
This lint flags this kind of member declaration.
BAD:
class C<X> {
final bool Function(X) fun; // LINT
C(this.fun);
}
void main() {
C<num> c = C<int>((i) => i.isEven);
c.fun(10); // Throws.
}
The problem is that X occurs as a parameter type
in the type of fun.
One way to reduce the potential for run-time type errors is to
ensure that the non-covariant member fun is
only used on this. We cannot strictly
enforce this, but we can make it private and add a forwarding
method fun such that we can check locally in the
same library that this constraint is satisfied:
BETTER:
class C<X> {
// ignore: unsafe_variance
final bool Function(X) _fun;
bool fun(X x) => _fun(x);
C(this._fun);
}
void main() {
C<num> c = C<int>((i) => i.isEven);
c.fun(10); // Succeeds.
}
A fully safe approach requires a feature that Dart does not
yet have, namely statically checked variance. With that, we
could specify that the type parameter X is
invariant (inout X).
It is possible to emulate invariance without support for
statically checked variance. This puts some restrictions on
the creation of subtypes, but faithfully provides the typing
that inout would give:
GOOD:
typedef Inv<X> = X Function(X);
typedef C<X> = _C<X, Inv<X>>;
class _C<X, Invariance extends Inv<X>> {
// ignore: unsafe_variance
final bool Function(X) fun; // Safe!
_C(this.fun);
}
void main() {
C<int> c = C<int>((i) => i.isEven);
c.fun(10); // Succeeds.
}
With this approach, C<int> is not a subtype
of C<num>, so c must have a
different declared type.
Another possibility is to declare the variable to have a safe but more general type. It is then safe to use the variable itself, but every invocation will have to be checked at run time:
HONEST:
class C<X> {
final bool Function(Never) fun;
C(this.fun);
}
void main() {
C<num> c = C<int>((int i) => i.isEven);
var cfun = c.fun; // Local variable, enables promotion.
if (cfun is bool Function(int)) cfun(10); // Succeeds.
if (cfun is bool Function(bool)) cfun(true); // Not called.
}
Enable
#
To enable the unsafe_variance rule, add
unsafe_variance under
linter > rules in your
analysis_options.yaml
file:
linter:
rules:
- unsafe_variance
If you're instead using the YAML map syntax to configure
linter rules, add unsafe_variance: true under
linter > rules:
linter:
rules:
unsafe_variance: true
除非另有说明,文档之所提及适用于 Dart 3.12.2 版本报告页面问题.