>[!note]
> Error Handling in [[C++]] is one of the *primary reasons* why I made [[Crab]].
One of the most prevalent ways to handle errors in [[C++]] (among many languages) is with the use of [[Exception|Exceptions]]. There are a lot of reasons that many dislike the use of exceptions (including me), but the main issue is the fact that functions don't need to be annotated with the fact that they can fail. One day I'll write everything I dislike about [[Exception|Exceptions]], but if you want to see more reasoning I recommended [Google's Official C++ Style Guide](https://google.github.io/styleguide/cppguide.html#Exceptions).
## When to use Error Handling?
Error handling is something you need to deal with when using or writing a *[[Fallible]] function* / operation, *eg.* a function that has some notion of *failure*.
A classic example of this would be implementing [[Linear Search]].
```cpp
i32 linear_search(const Vec<i32> &array, const i32 target) {
for (usize i = 0; i < array.size(); i++) {
// index i matches the target
if (array[i] == target) {
return i;
}
}
// failed to find an index
return -1;
}
```
In this case, the notion of this algorithm 'failing' would be when the given `array` has no index with the given `target`. In most examples, the 'error' is expressed by returning a [[../../../../02 Areas/Computer Science/Me Bitching about C++/Sentinel Value|Sentinel Value]], in this case $-1$ to represent that the search failed.
I have always disliked this (unless coding in [[C]] but what are ya gonna do).
When we use this function, to properly deal with the error we would have to check the function's returned value to see if it represents an error.
```cpp
i32 index = linear_search(Vec<i32>{0, 1, 42, 50, 69, 1}, 54);
if (index == -1) {
std::cerr << "Failed to search" << std::endl;
return;
}
std::cout << "Index " << index << " is 54 " << std::endl;
```
When this function returns, the value given will either be of *any* *valid* index value or a *single* *error* value.
## My Problem (and [[Crab]]'s solution) with [[../../../../02 Areas/Computer Science/Me Bitching about C++/Sentinel Value|Sentinel Values]]
A major issue I have with this is the fact that reading the function signature tells you nothing about what the return type implies, because at a base level - a [[../../../../02 Areas/Computer Science/Me Bitching about C++/Validity|valid]] $\color{white}\op{int}$ can be *any* number between $2^{-16}$ and $2^{16}$.
However, using an $\color{white}\op{int}$ to represent an error for `linear_search` means you are saying that a [[../../../../02 Areas/Computer Science/Me Bitching about C++/Validity|valid]] returned value is either an error, $-1$, or a positive integer.
>[!info]- Nerd way I wrote expressing this
>$\op{linearsearch} : \pa{x, t} \to r\in \set{\N^+, -1}$
As an extension of [[../../../../02 Areas/Computer Science/Me Bitching about C++/The Goal|my ultimate goal when writing code]], I want to be able to express this error more explicitly with a [[../../../../02 Areas/Computer Science/Me Bitching about C++/Type]] - so reading it's type tells you what you need to know.
This is where [[Crab]] comes in with it's implementation of [[Option<T>]].
[[Option<T>]] is a type that expresses the idea that a value could either be *something of type `T`*, or *nothing*.
Using [[Option<T>]] with `linear_search` would look like this:
```cpp
Option<usize> linear_search(const Vec<i32> &array, const i32 target) {
for (usize i = 0; i < array.size(); i++) {
// index i matches the target
if (array[i] == target) {
return crab::some(i);
}
}
// failed to find an index
return crab::none;
}
```
And checking for the error would instead look something like this:
```cpp
Option<usize> target_index = linear_search(Vec<i32>{0, 1, 42, 50, 69, 1}, 54);
if (target_index.is_none()) {
std::cerr << "Failed to search" << std::endl;
return;
}
// get the contained value
const usize index = target_index.get_unchecked();
std::cout << "Index " << index << " is 54 " << std::endl;
```
Using option, reading the the function's signature tells us that this function will *optionaly* return an `usize`, and using `Option<T>` allows us to be *explicit* about this -
as well as force you to check (or heavily remind you due to the syntax).
>[!question] If you want to know how [[Option<T>]] is implemented, where it is used *outside* of error handling, or how storing an optional value in memory works, visit [[Option<T>|its dedicated page]].
>[!tldr] Optional Values
> [[Crab]] did not invent the concept of [[Option<T>]], the [[Standard Template Library]] already has an implementation, `std::optional<T>`, along with most modern languages / standard libraries.
>
>Crab has its own verseions due to some shortcomings that `std::optional<T>` has, ie. less monadic operations, weaker invariants, and syntax that makes it a bit too convenient to ignore the possibility of errors for my liking.
## Complex Errors
Using an [[Option<T>]] for all errors won't always work. In the case of `linear_search` it makes sense because there is there is only *one way to fail*.
But would it make sense to use [[Option<T>]] for something like this?
```cpp
auto read_file_contents(const StringView path) -> Option<String> {
if (not std::filesystem::exists(path)) {
return crab::none;
}
std::ifstream file_stream{path};
// OS preventing us to read
// ( file is open by another program or insufficient permissions,
// we have no* way of knowing. )
if (file_stream.fail()) {
return crab::none;
}
StringStream file_contents{};
// trick to read all of a files contents into a stream without
// having to make a loop
file_contents << file_stream.rdbuf();
return file_contents.str();
}
```
If we only cared at a surface level that the file was either opened or closed and didn't care about details, this works perfectly fine.
*However*, what if someone comes along and wants to do something like try to read a file, and *if the file doesn't exist* - create it?
For whoever implements that feature, they have no way of distinguishing *how* the function failed - because they might want to fatally quit the program if the file itself can't be opened.
This is where [[Result<T, E>]] comes into place, which is the primary way you'll see errors in our [[../../../../02 Areas/Digipen/GAM200F24|GAM200F24]] project.
[[Result<T, E>]] can be thought of as identical to [[Option<T>]], however there are *more than one way to* "*spell*" `None`.
Analogous to [[Option<T>]], a value of type [[Result<T, E>]] can *either* be an `Ok` value or an `Err` (error) value.
This is what the example would like using [[Result<T, E>]] instead.
```cpp
enum class FileReadError {
NO_FILE_EXISTS,
OS_PREVENTED_OPEN
};
auto read_file_contents(const StringView path) -> Result<String, FileReadError> {
if (not std::filesystem::exists(path)) {
return crab::err(FileReadError::NO_FILE_EXISTS);
}
std::ifstream file_stream{path};
// OS preventing us to read
// ( file is open by another program or insufficient permissions,
// we have no* way of knowing. )
if (file_stream.fail()) {
return crab::err(FileReadError::OS_PREVENTED_OPEN);
}
StringStream file_contents{};
// trick to read all of a files contents into a stream without
// having to make a loop
file_contents << file_stream.rdbuf();
return crab::ok(file_contents.str());
}
```