iTranslated by AI
Who is Responsible for Handling Null?
How are you all spending your Golden Week? Since we are in a state where self-restraint and restrictions are forced upon us due to some mistakes by the powers that be, I've been staying indoors and playing around by building Gunpla.
By the way,
In the latter half of the link above, C#'s nullable reference types are mentioned[1].
Speaking of which, I was fortunately able to return to being a professional programmer at the end of last year, but in the Java code I wrote for the first time in N years, I had forgotten that null references become the so-called "NullPo" (NullPointerException) and struggled for a while until I remembered it[2].
Null Safety
In programming languages where "references" to values are possible as a specification, the problem of null references always follows. Some estimate the losses caused by null references at one billion dollars.
Controlling whether a variable is "null safe" at the compiler level is rational from the perspective of a mental model. C#'s nullable reference types can be seen as one such control. I wrote a blog post about "null safety" five years ago, so please refer to it if you're interested.
The most straightforward strategy is "not to allow null references in principle (and explicitly declare the type when they are allowed)," as seen in Swift, Kotlin, Dart, or the aforementioned C#. However, some programming languages take other strategies.
Rust Ownership
For example, Rust, currently the most trendy compiled language, has a mechanism called "ownership." There are three rules of ownership:
- Each value in Rust has a variable that’s called its owner.
- There can only be one owner at a time.
- When the owner goes out of scope, the value will be dropped.
Furthermore, borrowing (without moving ownership) can be performed using variables explicitly declared as reference types. Since attempting to use a variable that does not own or borrow a value results in a compilation error, "null safety" can be achieved to some extent using this mechanism.
Go has no "references", but...
Speaking of Go due to my personal preference, there are no "references" in the specification. Instead, there are pointer types that point to the addressing of a value. However, please note that the following types "behave like references."
| Type Name | Description Example |
|---|---|
| Channel | chan int |
| Interface | interface{} |
| Function | func(int) int |
| Slice | []int |
| Map | map[int]int |
Pointer types and the types listed above take nil as their initial value. While this can be considered a de facto null reference, unfortunately, Go cannot be called "null safe."
Interestingly, methods can be called even on a variable with a nil value. The following code results in neither a compilation error nor a runtime panic.
package main
import "fmt"
type Hello struct{}
func (h *Hello) String() string {
return "Hello, World!"
}
func main() {
var hello *Hello = nil
fmt.Println(hello.String())
// Output:
// Hello, World!
}
Of course, if you access the internal state within a method of a nil variable, it will result in a runtime panic.
package main
import "fmt"
type Hello struct {
Name string
}
func (h *Hello) String() string {
return "Hello, " + h.Name
}
func main() {
var hello *Hello = nil
fmt.Println(hello.String()) // panic: runtime error: invalid memory address or nil pointer dereference
}
In this way, in Go, the responsibility for handling null references can be placed on the method side depending on the method's design. I hope you can make good use of this.
References
-
It seems C#'s nullable reference types were introduced from version 8.0, released in 2019. However, static checking and warnings for null are optional and disabled by default. ↩︎
-
Part of it is because I've never written Go code for work, but I've never used a debugger for Go. I didn't need to. So, until I noticed the null reference bug in the Java code, I was forced to use a debugger for the first time in a long time. In embedded development, oscilloscopes and ICE + debuggers are essential tools, but I felt a bit of self-loathing thinking that having to use a debugger for a simple application is a sign of poor design. Well, since it's impossible to fully grasp code written by others, I often find myself relying on debuggers anyway. ↩︎
Discussion