Error Management: Go vs Rust vs Exception-based

I want to write some reference for error management in Rust and in Go, versus exception-class based language, maybe Python deserve a specific session, I will update that, but basically the point of exception-based error management is the sub-classing of a Exception class and the try {...} catch(...) {...} block for handling exceptional cases

Golang (aka Go language)

In Go Error is an interface. This is the only fact that one must understand about error. In Go a function can return a tuple that is de-structured by the assignment operator, like in

a, b, c := GetTuple(1)

Where

func GetTuple(i int) (int, int, int) {
   return i, i, i
}

And it is dev responsability to deal with function returning Error as a second component of the tuple.

The func that generates error must always returns a tuple of 2 component: (response, error), where either response or error must be nil, typically not both, but there is no limitation.

Error type is defined as:

type CustomError struct {
  S string
}
func (e *CustomError) Error() string {
  return e.S
}

An almost complete exposition of error handling is given by the golang tour https://go.dev/tour/methods/19

Rust Language

In Rust things are a bit more complicated. The main reason is the strong type system. In fact a fn in Rust can return a Result, that is an additive type, also known from C developer as the old and good enum (but with a better implementation). So:

pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

And E must implement fmt::Display.

Again, it is a dev responsability to deal with error, but there are some important constraints.

The fact that Result type is an additive type, means that only one case is given: you got a result, or you got an error. Both or neither are not possible, because the fn is just returning one value of the enum, being it Ok(T), or Err(E).

Of course the calling code must match both enum values, or if Ok(r) {...} else{...}, or if Err(e) {...}else{...}, or whatever, including question mark for throwing up to the caller of the caller.

These constraints are the gift offered by the Rust type system, and it would make your code more robust by design, at least if you are a responsible developer, and anyway one MUST deal with errors, someway, anyhow

Exception based language

Basically one has a Exception base class then there 2 main constructs:

  1. raise or throw exception
  2. try…catch, or try…except block

If the code do not catch the exception, depending on the strength of the language, it may not compile (/run) at all, or just crash at runtime.

(semi) Conclusion

I would like to write a more detailed article, but also I want this to remains succinct.

I want to notice here that Rust type system play an important role in error handing, but also the specific construct given by ? (question mark) and the functional nature of the language make it really robust, but of course robustness come with a price to charged to the head of the developer, question mark and anyhow are very practical to procrastinate the error management, but constraint on Err(e) type may be annoying on first sight (it must impl fmt::Display).

On contrast, Go language is very relaxed on error handling, still it is expected to provide a struct implementing the interface Error, and this is almost everything one must remember, it is idiomatic to see if err != nil everywhere in a Go source code.

From the performance point of view, both Go and Rust has the most lightweight method for dealing with errors. By contrast exception based language needs specific runtime that is almost hidden to the developer, that is not true for Go, neither for Rust, meaning literally: the error handing cost is not hidden in Go/Rust.


Posted

in

by

Tags: