[Coding] An error != nil in Go but ended up showing Rust’s Result<T,E>
Likely 90% of everything could fail when your code does something. For example, a function that reads content from a file…
![[Coding] An error != nil in Go but ended up showing Rust’s Result<T,E>](/content/images/size/w1200/2025/03/1-pauxhqm5-j9jygmocyvvzw.png)
An error != nil in Go but ended up showing Rust’s Result<T,E>
Let me start the post by quoting a paragraph from the article here.
A common point of discussion among Go programmers, especially those new to the language, is how to handle errors. The conversation often turns into a lament at the number of times the sequence
if err != nil {
return err
}
shows up. We recently scanned all the open source projects we could find and discovered that this snippet occurs only once per page or two, less often than some would have you believe. Still, if the perception persists that one must type
if err != nil
all the time, something must be wrong, and the obvious target is Go itself.
I myself learned Go a long time ago, must admit that i felt cumbersome when it comes to error handling in Go as a beginner. However, back then i was a novice and didn’t realize the bigger picture. Now i’m experienced, i feel thankful that new languages really put in effort into error handling and make it a really good experience. Here’s why
Operations that could fail
As a beginner, my coding lack of consistency and convention so i put try/catch
block everywhere. Soon, this is the default way how i declare the function back then.

For me, the try/catch
block is like the childhood friend which i used to play with most of the time when i was a child but somehow stopped hanging out when grown up.
The distinction when to use try/catch
and when to throw Exception (In languages other than Go) and terminate the process is very important.
Likely 90% of everything could fail when your code does something. For example, a function that reads content from a file looks harmless but could fail for many reasons such as “File not found”, “Disk failure”, “Some other process is holding the file… Back to the error handling and throwing exception, we have a very important question to ask.
Is it recoverable?
For example, performing a quick check if the file is downloaded, we can recover by downloading the file? In this case, the error is recoverable, we can handle it and continue the work. On the other hand, unrecoverable cases like missing configuration file when the web server starts we would rather throws exception than trying to something else.
Is the error != nil unnecessary then?
At first, it seems like the error != nil is a repetition. But, would you rather kill the process or checking for errors and handle them gracefully?
The error != nil is not only a try/catch block
What if the catch block tries to recover but fails? Are we gonna try/catch
in a try/catch
block? Do you remember the callback hell when working with JS in the early day?

The new mechanism that abstract away the try/catch
block in general and error in Go in particular is just like the evolve of Promise
and async/await
in JS.
The subtle but important point between try/catch
and error
is that we make it explicit. You can call a function and forget that it could throw exception and terminate your process by throwing an exception but error is an explicit output and it should be your fault if you ignore it.

Errors that could be modeled
Instead of using try/catch block all over the way, we can model our error by introducing new type. In Go we have error. In Rust, we also have Result<T,E>. In C#, i’ve seen things like HttpResponseMessage
that encapsulate the result of an HTTP request instead of throwing exception.
The ergonomic and explicit flow that Rust provides
I really like Rust, so i will introduce the way this language handle the error.
It has the Result<T,E> type that could be Ok(T) which is a success or Err(E) which means a failure.

Instead of returning the value like other old languages, we return the Result<T,E> in which T is the data we want to return if the operation is success and E is the error returned when the operation failed.
The beauty of this design is that we cannot take shortcut and access the inner value or error directly but have to use an operator called pattern matching

We can also propagate error from the inner function to the parent function by using the ? operator.

It’s cleaner than the try/catch :) More over, the language also provide Error trait (quite similar to Interface in other languages). It not only represent error but also provides abilities to get the context providing APIs to get the source, description, cause, provide. Very well modeling, indeed.

Conclusion
In this post, i hope you understand how important it is to handle error, when to throw exception and abort the process. However, there are other ways to handle error instead of try/catch everywhere 😉
If you like the post, please consider giving me a clap and follow me for updates on future posts.
If you want to support me, please consider buying me a coffee.
