Errors #
We all make mistakes when we write code.
Developing efficient strategies to identify mistakes is an essential part of programming.
Compile time vs runtime errors #
In (pre)compiled languages (like Java, C/C++, C#, Python, Rust, Go, etc.), a distinction is generally made between compile time errors and runtime errors.
- A compile time error prevents a program from compiling. It can be thought of as a “syntactic” error.
- A runtime error (a.k.a. bug) occurs during the execution of a program. It may depend on the input of the program.
Hint. Compile time errors tend to be (much) easier to fix than bugs, especially in large projects. Besides, an IDE can identify some compile time errors (and highlight them) before the program is even compiled.
So it is often good practice to write your code in such a way that a maximal number of errors can be identified at compile time.
Note. For interpreted languages (like Javascript, Lua, etc.), the term “compile time error” is meaningless. However, a distinction can still be made between errors that can be preemptively identified (e.g. highlighted by an IDE), and errors that can only be detected at runtime.
Error messages #
A precise error message can significantly simplify debugging.
As a toy example, consider the following Java program:
public class City {
String name;
int zipCode;
public City(String name, int zipCode) {
this.name = name;
this.zipCode = zipCode;
}
}
|
|
In this program, there are several reasons why the method MyClass.myMethod
may fail at runtime.
Can you identify some of them?
- the input array may be null,
- the input array may have length 0,
- the input array may be empty,
- the first city in this array may have a zip code $\ge$ 20 000 and no name.
In the above example, if we execute the following,
myMethod(null);
then we obtain this error message:
java.lang.NullPointerException: Cannot load from object array because "cities" is null
at MyClass.myMethod(MyClass.java:5)
Note that this message provides a clear indication of:
- the file (
MyClass.java
), method in this file (MyClass.myMethod
) and line of code (5) that caused the error, - the type of error (
NullPointerException
), - the cause of the error (the variable
city
has valuenull
).
Hint. A
NullPointerException
in Java often indicates an attempt to access an object (or array) via a pointer (e.g. a variable) whose value isnull
.
Similarly, if the method is called with an empty array,
myMethod(new City[]{});
we get the following error message:
java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at MyClass.myMethod(MyClass.java:5)
Which type of error will the following call to myMethod
produce?
myMethod(new City[]{ new City(30100, null) });
java.lang.NullPointerException: Cannot invoke "String.length()" because "firstCity.name" is null
at MyClass.myMethod(MyClass.java:7)
Anticipation #
Consider the following method:
int least(int x, int y){
return x <= y ?
x : y;
}
This method has two desirable features (shared with mathematical functions):
- any arguments for this method (i.e. any pair of integers in this example) are a valid input,
- its execution only depends on its arguments, i.e. if the method is called twice with the same arguments, then it has the same behavior.
However, this is not the case of all methods.
If one of these two condition does not hold, then it may be useful to write your own error messages, in anticipation. This may benefit:
- users of the program, and
- collaborators (including your future self!) who will maintain (debug, extend, etc.) the program.
Example. For instance, consider a method
boolean isSolvable (int[][] grid);
that takes as input a grid of sudoku, and returns
true
iff it is solvable, meaning that this grid admits a unique solution. A valid input for this problem could be a 9 x 9 array of integers with values between0
and9
(where0
indicate the absence of value). However, Java does not provide such a precise data type.It may be useful to produce an (informative) error message if the input array:
- is not
9 x 9
, or- contains a value
< 0
or> 9
.
Controlled environment #
Anticipating all types of invalid inputs/scenarios (and producing error messages for these) may induce a lot of boilerplate code. If you have control over the input and/or execution environment of a method, then this may not be necessary. For instance, in the case of auxiliary methods (i.e. methods that are not visible to other classes or components).
Example (continued). Consider the method
boolean isSolvable (int[][] grid)
described above. Let us assume that it is private, and called only by a methodgenerateGrid
that generates a grid pseudo-randomly.It may be safe to assume that the implementation of
generateGrid
only produces9 x 9
arrays with values between0
and9
. In this scenario, there is probably no need to implement error messages inisSolvable
.