Even if I declare my list as fixed-length List, Dart cannot check array bound in compile time.
void main() {
var fixed = List<int>(5);
fixed[5000] = 3; //Runtime error!
}
I tested in DartPad. Why Dart cannot check array bound in compile time?
Related
Learning Dart and using dart_code_metrics to ensure that I write code that meets expectations. One of the rules that is active is avoid-non-null-assertion.
Note, the code below was created to recreate the problem encountered in a larger code base where the value of unitString is taken from a JSON file. As such the program cannot control what is specified in the JSON file.
From pubspec.yaml
environment:
sdk: '>=2.15.0 <3.0.0'
// ignore_for_file: avoid_print
import 'package:qty/qty.dart';
void main() {
const String unitString = 'in';
// unit.Width returns null if unitString is not a unit of Length.
if (Length().unitWith(symbol: unitString) == null) {
print('units $unitString not supported.');
} else {
// The following line triggers avoid-non-null-assertion with the use of !.
final Unit<Length> units = Length().unitWith(symbol: unitString)!;
final qty = Quantity(amount: 0.0, unit: units);
print('Qty = $qty');
}
}
If I don't use ! then I get the following type error:
A value of type 'Unit<Length>?' can't be assigned to a variable of type 'Unit<Length>'.
Try changing the type of the variable, or casting the right-hand type to 'Unit<Length>'.
Casting the right-hand side to
Unit<Length>
fixes the above error but cause a new error when instantiating Quantity() since the constructor expects
Unit<Length>
and not
Unit<Length>?
I assume there is an solution but I'm new to Dart and cannot formulate the correct search query to find the answer.
How can I modify the sample code to make Dart and dart_code_metrics happy?
Your idea of checking for null before using a value is good, it's just not implemented correctly. Dart does automatically promote nullable types to non-null ones when you check for null with an if, but in this case you need to use a temporary variable.
void main() {
const String unitString = 'in';
//Use a temp variable, you could specify the type instead of using just using final
final temp = Length().unitWith(symbol: unitString);
if (temp == null) {
print('units $unitString not supported.');
} else {
final Unit<Length> units = temp;
final qty = Quantity(amount: 0.0, unit: units);
print('Qty = $qty');
}
}
The basic reason for that when you call your unitWith function and see that it's not null the first time, there's no guarantee that the when you call it again that it will still return a non-null value. I think there's another SO question that details this better, but I can't seem to find.
void main() {
List a = ["aa", "bbb", "ccccc"];
Iterable b = a.iterator;
while (b.moveNext()) {
/*The method 'moveNext' isn't defined for the type 'Iterable'.
Try correcting the name to the name of an existing method,
or defining a method named 'moveNext'.dartundefined_method
*/
print(b.current);
/* The getter 'current' isn't defined for the type 'Iterable<dynamic>'.
Try importing the library that defines 'current',
correcting the name to the name of an existing getter,
or defining a getter or field named 'current'.dartundefined_getter
*/
}
}
Multiple problems. E.g. a.iterator does not return a Iterable but instead Iterator. Also, you should not write List since that means List<dynamic> in Dart which means you are loosing type information.
The easiest way is to just use final or var and let Dart automatically use the most precise type (e.g. List<String>):
void main() {
final a = ["aa", "bbb", "ccccc"];
final b = a.iterator;
while (b.moveNext()) {
print(b.current);
}
}
Why is it an error in Dart to try to set a class's static variable in the global space?
Example:
class Name {
static String? firstName;
}
Name.firstName = 'Mike'; // Error
void main() {
Name.firstName = 'Mike'; // Ok
}
It's not a big deal. I just came across this and then couldn't find an explanation for why it is. Where in the documentation does it describe the nuance here?
[UPDATE]
The actual error thrown is, among others: "Variables must be declared using the keywords 'const', 'final', 'var', or a type name."
You actually can execute statements outside of a function, but they have to be statements that declare scoped variables. Maybe these aren't technically statements, but just variable instantiations.
class Name {
static String? staticName;
String? lastName;
}
final me = Name(); // Ok
me.lastName = 'Jones'; // Error
void main() {
Name.staticName = 'Mike'; // Ok
final you = Name(); // Ok
you.lastName = 'Smith'; // Ok
}
Without the variable scoping, the compiler thinks I must be defining a function and it gets confused when there is no parameter list or function body.
It makes sense that statements are restricted to variable instantiations of function definitions only, so that there won't be side effects related to execution order to other importers of the file, as per #jamesdlin answer.
Name.firstName = 'Mike'; is a statement. You can't execute arbitrary statements in the global namespace. In what order would they execute? Suppose you had:
name.dart:
class Name {
static String? firstName;
}
and mike.dart:
import 'name.dart';
Name.firstName = 'Mike';
and spike.dart:
import 'name.dart';
Name.firstName = 'Spike';
and finally:
import 'name.dart';
import 'mike.dart';
import 'spike.dart';
void main() {
print(Name.firstName);
}
What should happen? Should it be illegal for multiple libraries to assign to Name.firstName? Should the last one imported win? If so, then suddenly importing a library would have side-effects, and order would matter. What would happen if an imported library imports other libraries with side-effects?
It's a huge headache that is completely unnecessary since you could have just done:
class Name {
static String? firstName = 'Mike';
}
in the first place.
I'm using zig 0.7.0. and I'm trying to import a list of zig source files from an array. Each source file has a main function (whose return type is !void) that I would like to call. The array module_names is known at compile time.
Here is what I tried to do:
const std = #import("std");
const log = std.log;
const module_names = [_][]const u8{
"01.zig", "02.zig", "03.zig", "04.zig", "05.zig",
};
pub fn main() void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
for (module_names) |module_name, i| {
const module = #import(module_name); // this fails
log.info("i {}", .{i});
try module.main();
}
}
Even if the array is known at compile time, #import(module_name) gives me this error:
./src/main.zig:13:32: error: unable to evaluate constant expression
const module = #import(module_name);
^
./src/main.zig:13:24: note: referenced here
const module = #import(module_name);
I could understand the error if the array would be dynamically generated and only known at runtime, but here the module_names array is known at compile time. So I am a bit confused...
Alternatively, I also tried to wrap the entire main body in a comptime block:
pub fn main() void {
comptime {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
for (module_names) |module_name, i| {
const module = #import(module_name); // no errors here
log.info("i {}", .{i});
try module.main();
}
}
}
Here #import(module_name) gives me no errors, but the log.info fails with this other error:
/home/jack/.zig/lib/zig/std/mutex.zig:59:87: error: unable to evaluate constant expression
if (#cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)
^
/home/jack/.zig/lib/zig/std/mutex.zig:65:35: note: called from here
return self.tryAcquire() orelse {
^
/home/jack/.zig/lib/zig/std/log.zig:145:60: note: called from here
const held = std.debug.getStderrMutex().acquire();
^
/home/jack/.zig/lib/zig/std/log.zig:222:16: note: called from here
log(.info, scope, format, args);
^
./src/main.zig:26:21: note: called from here
log.info("i {}", .{i});
Is this kind of dynamic import possible in zig?
As of Zig 0.8.0, the operand to #import is required to be a string literal.
A Zig compiler wants to know all the possibly imported files so that it can eagerly go find them and compile them when you kick off a compilation process. The design of the language is constrained by making it possible for a fast compiler to exist.
So what can we do? I think this accomplishes the task in an equivalent manner:
const std = #import("std");
const log = std.log;
const modules = struct {
pub const module_01 = #import("01.zig");
pub const module_02 = #import("02.zig");
pub const module_03 = #import("03.zig");
pub const module_04 = #import("04.zig");
pub const module_05 = #import("05.zig");
};
pub fn main() void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
inline for (#typeInfo(modules).Struct.decls) |decl, i| {
const module = #field(modules, decl.name);
log.info("i {d}", .{i});
try module.main();
}
}
And the neat thing here is that, indeed, the compiler is able to eagerly fetch all 5 of those files and kick-start the compilation process, even before running the compile-time code to determine which one actually gets imported. Win-win.
I think the kind of import you're after is possible, my understanding is that #import is taking a zig source file and turning it into a struct type. It actually seems like something that could even be done at runtime (although not using #import, which wants a comptime parameter (this is the important part for your problem).
The reason why your first example fails is that the argument you're passing, module_name isn't known at comptime, your for loop won't execute until your program runs.
Your instinct to solve the problem is correct, get the loop to evaluate at compile time (specifically the capture value and iterator); I think there are two things you could do fix it.
Wrapping the loop in a comptime block as you have done will work, but you'll need to think about what exactly it means to evaluate expressions at compile time, and if it makes sense. I think the implementation of log will prevent you from logging at compile time, so you'd need to collect the values you're interested in inside the loop, and log them once outside the comptime block.
The other way you could fix it is to force the capture values of the loop to be evaluated at compile time by unrolling the loop using an inline for:
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
inline for (module_names) |module_name, i| {
const module = #import(module_name);
log.info("i {}", .{i});
try module.main();
}
}
Disclaimer: There might be a better way of doing this, I'm comparatively new to the language =D
Currently building a sample whiteboard project and faced a typescript issue
var format = (formatString, ...params: any[]): string => {
var i = 0;
while (/%s/.test(formatString)) {
formatString = formatString.replace('%s', arguments[++i]) --> Error at (arguments)
}
return formatString;
};
I checked the similar issue over stack overflow but didn't get a concrete answer
You are confusing the availability of rest parameters feature in JS an in typescript.
The fact that arguments object is used for functions with variable number of parameters in ES3 & ES5 should not make you use it in typescript.
For functions with variable number of parameters in typescript, you should use rest parameters. Typescript compiler will compile it to:
Js rest parameters with ES2015 target
arguments object with ES3 or ES5 target
var format = (formatString: string, ...params: any[]): string => {
var i = 0;
while (/%s/.test(formatString)) {
formatString = formatString.replace('%s', params[i++]);
}
return formatString;
};
Playground Link