Dart const class argument checking - dart

How does one perform argument checking on const class?
For example:
class Data {
String value;
const Data(this.value);
}
How can I check that for example value.contains("banana") == true?
If I try to check it in assert like below, linter reports error Invalid constant value. dart(invalid_constant)
class Data {
String value;
const Data(this.value)
: assert(value.contains("banana");
}

class Data {
String value;
const Data(this.value)
: assert(value.contains("banana");
}
Well for one, you are missing a parenthesis after your assertion. For another, in a constant class, all the fields must be marked as final.
But the last thing (which is what is actually relevant to your question) is that if your constructor is marked as const, then all values and operations in your assertions have to be constants. This is because constant instances are initialized at compile-time and the compiler can't perform assertions where it has to run code in order to validate the constructor data.
This means you can perform things like equality or other boolean operator checks against other constant values:
: assert(value == 'banana');
But you can't compare against non-constant values or do things like call methods (note: the shown errors are not the errors that the compiler will actually report):
: assert(value == Foo()); // Error: Foo is not a constant
: assert(value.contains('banana')); // Error: method calls are not a constant operation
: assert(value == const Bar()); // Error: Bar is not a compiler-known constant
That last error may be a little obtuse seeing as Bar is a constant class object. The reason it doesn't work, though, is because in order to implement == (the boolean equals operator), Bar has to define code for that operator, and the compiler can't run code in constant assertions. The end result is that, even if an object is marked as a constant, it still can't be used in an assertion. You can only use primitive constants, i.e. Null, bool, int, double, String.

Related

How does null assertion work in dart and when can I use it?

Can someone simply explain to me how null assertion (!) works and when to use it?
The ! operator can be used after any expression, e!.
That evaluates the expression e to value v, then checks whether v is the null value. If it is null, an error is thrown. If not, then e! also evaluates to v.
The static type of an expression e! is (basically) the static type of e with any trailing ?s remove. So, if e has type int?, the type of e! is int.
You should not use e! unless e can be null (the type of e is potentially nullable).
The ! operator is dynamically checked. It can throw at runtime, and there is no static check which can guarantee that it won't. It's like using a value with type dynamic in that all the responsibility of preventing it from throwing is on the author, the compiler can't help you, and you need good tests to ensure that it won't throw when it's not supposed to.
It's called an assertion because it should never throw in production code.
So, use e! when you know (for some reason not obvious to the compiler, perhaps because of some invariant guaranteeing that the value is not null while something else is true) that e is not null.
Example:
abstract class Box<T extends Object> {
bool hasValue;
T? get value;
}
...
Box<int> box = ...;
if (box.hasValue) {
var value = box.value!;
... use value ...
}
If you are repeatedly using ! on the same expression, do consider whether it's more efficient to read it into a local variable just once.
Also, if (like this Box example) the value being null is equivalent to the other test you just did, maybe just check that directly:
Box<int> box = ...;
var value = box.value;
if (value != null) {
... use value ...
}
This code, with an explicit != null check on a local variable, is statically guaranteed to not throw because the value is null.
The code using ! above relies on the author to maintain whichever invariant allowed them to write the !, and if something changes, the code might just start throwing at runtime. You can't tell whether it's safe just by looking at the code locally.
Use ! sparingly, just like the dynamic type and late declarations, because they're ways to side-step the compiler's static checking and ensure it that "this is going to be fine". That's a great feature when you need it, but it's a risk if you use it unnecessarily.

is there a difference between implicit cast vs 'as' keyword in dart?

Is there any difference between using an implicit cast to cast in dart vs the 'as' keyword? Will they result in the same (or similar) runtime error if the type is not as expected?
For example:
dynamic foo = "blah";
String boo = foo; // is this
String boo2 = foo as String; // the same as this?
No. And yes.
TL;DR: Don't worry about the difference, just do what reads the best.
If your program is correct, and the casts will succeed, then there is unlikely to be any difference.
When inferring types, String boo = foo; will infer the type of foo with a context type of String. If the resulting static type of foo then turns out to be dynamic then it implies an implicit downcast from dynamic to `String.
For String boo = foo as String;, the static type of foo is inferred with no context type. No matter what the resulting static type is, it will be cast to String at run-time.
You can see a difference between these two if you have a more complicated expression than just the variable foo:
T first<T extends dynamic>(List<T> list) => list.first;
String boo = first([1]); // <- compile-time error
String boo2 = first([1]) as String;
With this example, you get a compile-time error in the boo line because the compiler knows that the list should be a List<String>. There is no error in the boo2 line because the list only needs to be a List<dynamic>, and whatever first returns is then dynamically cast to String.
A more contrived example would be:
T firstOrDefault<T extends dynamic>(List<T> list) {
if (list.isEmpty) {
// Invent some default values for known types.
if (null is T) return null as T;
if (0 is T) return 0 as T;
if (0.0 is T) return 0.0 as T;
if ("" is T) return "" as T;
if (false is T) return false as T;
throw UnsupportedError("No default value for the needed type");
}
return list.first;
}
String boo = firstOrDefault([]); // <- returns "", with null safety.
String boo2 = firstOrDefault([]) as String; // <- returns null, throws.
(Doing that kind of type-parameter specialization is not a recommended programming style. It's too fragile precisely because it can be affected in unpredictable ways by subtle changes to static types.).
Ignoring inference and static checking, there is not much difference at run-time. If foo is just a simple expression with static type dynamic, then the language requires downcast to String in both situations.
However, the Dart2JS web compiler can enable unsound optimizations which basically omit implicit downcasts entirely (as an "optimization" assume that they would have succeeded) and the go on with potentially type-unsound values flowing around.
For that reason, some people (mainly those coding for the web) may prefer to use implicit downcasts over explicit downcasts.
Dart with null safety only has implicit downcasts from dynamic.
You can always force an implicit downcast from any type by doing:
String boo3 = foo as dynamic;
The as dynamic is a free up-cast, it has no effect at run-time (it can't fail and the compiler knows that), so all it does is change the static type of the expression ... to something which introduces an implicit downcast, which the dart2js compiler will then (unsoundly) ignore as well.
(Use with caution, as with everything involving dynamic. Also, the analyzer might warn about an "unnecessary up-cast".)

How to simulate undefined in Dart

In JS, there is undefined and null. Undefined means "no value", null means "value equivalent to emptiness".
In Dart however (and possibly in other languages, I don't know, but right now I'm using Dart), there is no undefined, only null. Therefore it is impossible to make the distinction between a value equivalent to emptiness and the absence of value.
Is there a standard way of simulating this difference in Dart?
No. The null value (of type Null) is the only built-in value that represents the absence of a value. It does not distinguish on why the value is absent.
It's also (almost) the only type you can combine with another type directly in the type system. With null safety, you'll be able to write int? for int-or-Null.
If you want another marker for absence, you can use (or write) an Option type like:
abstract class Option<T> {
final T value;
bool get hasValue => true;
Option(this.value);
factory Option.none() => const _OptionNone();
}
class _OptionNone implements Option<Never> {
const _OptionNone();
bool get hasValue => false;
T get value => throw UnsupportedError("None option has no value");
}
Then you can represent either value or no value.
(There are existing implementations of such a class, for example in the quiver package).

Passing a Function to an annotation in dart

I want to send a Function as a parameter to an annotation like this:
#JsonKey(fromJson: ...)
final int variable;
where fromJson is a Function, but it gives me this error:
Arguments of a constant creation must be constant expressions.
what is the solution? any help would be greatly appreciated.
You didn't write what you wanted for ..., and that's the part that causes the problem.
The argument to the fromJson parameter must be a compile-time constant value because annotations must be constant.
The only constant function values are top-level or static functions, so you need to declare the function type want, let's say as static:
class MyClass {
#JsonKey(fromJson: _variableFromJson)
final int variable;
static int _variableFromjson(dynamic json) => ...;
...
}
You can't write the function in-line as (fromJson: (json) => ...) because function expressions are not compile-time constant.

Way to defensive check value assigned to public const variable in immutable class in C++17?

Coming back to C++ after a hiatus in Java. Attempting to create an immutable object and after working in Java, a public const variable seems the most sensible (like Java final).
public:
const int A;
All well and good, but if I want to defensive check this value, how might I go about it. The code below seems strange to me, but unlike Java final members, I can't seem to set A in the constructor after defensive checks (compiler error).
MyObj::MyObj(int a) : A(a) {
if (a < 0)
throw invalid_argument("must be positive");
}
A public const variable for A seems like a clearer, cleaner solution than a getter only with a non const int behind it, but open to that or other ideas if this is bad practice.
Your example as it stands should work fine:
class MyObj {
public:
const int var;
MyObj(int var) : var(var) {
if (var < 0)
throw std::invalid_argument("must be positive");
}
};
(Live example, or with out-of-line constructor)
If you intend that MyObj will always be immutable, then a const member is
probably fine. If you want the variable to be immutable in general, but still have the possibility to overwrite the entire object with an assignment, then better to have a private variable with a getter:
class MyObj {
int var;
public:
MyObj(int var) : var(var) {
if (var < 0)
throw std::invalid_argument("must be positive");
}
int getVar() const { return var; }
};
// now allows
MyObj a(5);
MyObj b(10);
a = b;
Edit
Apparently, what you want to do is something like
MyObj(int var) {
if (var < 0)
throw std::invalid_argument("must be positive");
this->var = var;
}
This is not possible; once a const variable has a value it cannot be changed. Once the body ({} bit) of the constructor starts, const variables already have a value, though in this case the value is "undefined" since you're not setting it (and the compiler is throwing an error because of it).
Moreover, there's actually no point to this. There is no efficiency difference in setting the variable after the checks or before them, and it's not like any external observers will be able to see the difference regardless since the throw statement will unroll the stack, deconstructing the object straight away.
Generally the answer by N. Shead is the regular practice - but you can also consider:
Create domain-specific types and use them instead of general primitives. E.g., if your field is a telephone number, have a type TelephoneNumber which, in its constructor (or factory), taking a string, does all the telephone number validation you'd like (and throws on invalid). Then you write something like:
class Contact {
const TelephoneNumber phone_;
public:
Contact(string phone) : phone_(phone) { ... }
...
When you do this the constructor for TelephoneNumber taking a string argument will be called when initializing the field phone_ and the validation will happen.
Using domain-specific types this way is discussed on the web under the name "primitive obsession" as a "code smell".
(The problem with this approach IMO is that you pretty much have to use it everywhere, and from the start of your project, otherwise you start having to have explicit (or implicit) casting all over the place and your code looks like crap and you can never be sure if the value you have has been validated or not. If you're working with an existing codebase it is nearly impossible to retrofit it completely though you might just start using it for particularly important/ubiquitous types.)
Create validation methods that take and return some value, and which perform the validation necessary - throwing when invalid otherwise returning its argument. Here's an example validator:
string ValidatePhoneNumber(string v) {
<some kind of validation throwing on invalid...>
return v;
}
And use it as follows:
class Contact {
const string phone_;
public:
Contact(string phone) : phone_(ValidatePhoneNumber(phone)) { ... }
I've seen this used when an application or library is doing so much validation of domain-specific types that a small library of these domain-specific validator methods has been built up and code readers are used to them. I wouldn't really consider it idiomatic, but it does have the advantage that the validation is right out there in the open where you can see it.

Resources