I was writing software in Go for a year before I switched to Rust. I've not felt a need to touch Go since. Basically, anything you can do in Go, you can also do in Rust, but Rust will let you do it with higher efficiency and with significantly less lines of code. In the end, it's just easier to write software with Rust than it is Go.
Feature-wise, Rust features generics and functional programming via higher-order functions and iterators, which is something that Go especially lacks in. Go doesn't have nice concepts like `Option`, `Result`, or `Iterator`. That's not something I'd personally want to live without today. The Go method is effectively writing boiler plate code everywhere, which leads to much room for error prone implementations that require more testing.
I haven't felt that Rust was more complex than Go, at least when you're actually writing software in Rust. Rust libraries feature semantic versioning and are automatically downloaded and verified at build time based on the contents of your `Cargo.toml` and `Cargo.lock` files. No importing of Git repositories directly required. Go does not provide an equal on that front.
There are a lot of great libraries out there to bring you extreme performance, simply, such as the bytecount crate, which is just a library that features a single function, a function that counts the occurrence of a specific byte, 32 bytes at a time with AVX, with additional SSE/SIMD implementations depending on what the processor supports.
All there is to truly know about Rust is the borrowing and ownership mechanism and how to implement a custom `Iterator`. If you have a solid understanding of both then you've pretty much mastered all you need to know about Rust.
The borrowing and ownership mechanism can be simplified down to:
- Passing a variable by value will move ownership, dropping the original variable from memory
- Passing a variable by mutable reference will keep the original variable, but allow you to modify the variable.
- You may only borrow a variable mutably once at a time, and you may not immutably borrow while mutably borrowing.
- You may have as many immutable borrows as you want, so long as you aren't modifying that value.
- You may mutably borrow a field in a struct, and then mutably borrow a different field in the same struct simultaneously, so long as you aren't also mutably borrowing the overall struct.
- You can use `Cell` and `RefCell` to allow for mutably modifying an immutable field in a struct.
- You may mutably borrow multiple slices from the same array simultaneously so long as there is no overlap.
- Safe memory practices means that instead of mutably borrowing the same variable in multiple places, you queue the changes to make in a separate location and apply them serially one after another.
Then for the `Iterator` trait, you would know that all traits have required methods, whereby as long as you implement the required methods for your type, you will automatically gain all of the other methods associated with the trait. For the `Iterator` type, you only need to implement the `next` method, and that looks something like so:
```
struct DataIterator<'a> {
data: &'a [u8],
index: usize,
}
enum Token<'a> {
One(&'a [u8]),
Two(&'a [u8])
}
impl<'a> Iterator for DataIterator<'a> {
type Item = Token<'a>;
fn next(&mut self) -> Option<Token<'a>> {
let start = self.read;
for element in self.data.iter().skip(self.read) {
self.read += 1;
// if next value is found then return Some(&value[start..self.read])
}
None
}
}
```