Chapter 1: Characteristics of Rust
Rust was developed primarily by Mozilla, the developer of Firefox. It emphasizes safety, such as not allowing pointers to invalid memory regions. It also does not have a complex runtime like garbage collection. In Stack Overflow surveys, it ranked first for three consecutive years from 2016 to 2018 as the “most loved language.” LLVM is adopted as the compiler backend. On several environments including x86 Linux, static linking with external libraries is also supported.
Let’s also look at adoption cases. For Dropbox’s distributed storage system Magic Pocket, the first version was written in Go and later rewritten in Rust. Domestically, Dwango adopted it for developing the distributed object storage Frugalos. The user authentication part of npm, the JavaScript package registry, is also written in Rust. AWS developed Firecracker, a secure virtual machine for serverless computing.
Chapter 2: First Rust Program
The Rust toolchain consists of the Rust compiler rustc, the package manager cargo,
and the standard library std.
Using rustup, you can manage multiple toolchains such as stable and nightly versions.
Also, by creating a file called rust-toolchain in the package’s top directory
and writing nightly in it, you can use nightly for that package.
The nightly version includes experimental features and is released every night.
The .rustup directory is managed by rustup, and .cargo is managed by cargo.
The former contains the toolchain itself, while the latter contains the rustc, cargo, and rustup commands.
Use it with source ~/.cargo/env.
Create a new package with cargo new --bin hello.
Things ending with ! like println! are macros, so they are evaluated and expanded in the early stages.
x.func() is method call syntax and is interpreted as func(&x).
In fn hoge<F>(..) where F: trait bound, F can be any type that satisfies the trait bound shown in the where clause.
For debugging, it’s more convenient to use rust-lldb or rust-gdb rather than using GDB directly.
There are incompatible editions (two types: 2015 edition and 2018 edition), which can be specified in Cargo.toml on a per-crate basis.
By selecting the musl target, you can create binaries statically linked with external libraries.
Chapter 3: Quick Tour
Identifiers such as functions, variables, and constants should be in snake_case, and type parameters should be a single uppercase letter.
There’s an automatic formatter rustfmt, which is convenient to use.
For error messages, you can investigate the cause and solution with rustc --explain 308.
Methods are implemented in impl blocks.
Closures automatically implement Fn, FnMut, or FnOnce traits depending on the context.
Traits like PartialEq and Debug can be automatically derived with the #[derive(Debug, PartialEq)] attribute.
A platform-independent multithreading API is provided as a standard library.
Using the Rayon crate developed by Nicholas Matsakis, a core developer of the Rust compiler,
you can easily and safely implement parallel data processing.
The fact that a value of a type implementing the Sync trait means it is always memory-safe even when used in parallel from multiple threads.
You cannot split x into two mutable borrows with &mut x[..mid_point],
so use the split_at_mut() method instead.
Chapter 4: Primitive Types
Types built into the language are called primitive types.
The unit type is a type that represents only emptiness, has only one value, and is represented by the empty tuple ().
Integer literals are interpreted as i32 type by default, but you can specify the type explicitly by adding a suffix like 0u8.
Overflows in arithmetic operations and shift operations are not detected in release mode.
The character type holds one Unicode character.
The length of an array is determined at compile time. If you want to change the length at runtime, use the Vec<T> type.
When using slice methods on arrays, they are implicitly coerced.
str returns the number of utf-8 bytes, not the number of characters.
Chapter 5: User-Defined Types
Since str cannot add, delete, or modify characters, use String to modify freely.
This is similar to the relationship between immutable slices and vectors.
By adding &, it is converted from &String to &str by type coercion.
Also, String owns the actual data, while &str borrows (references) the data without owning it.
You can explicitly cast types using as, as in let y = x as f64 / 2.5.
However, since as only supports conversions between scalars, it cannot be used for tuples or arrays.
The try! macro was sometimes used for error handling, but now the ? operator is recommended.
There’s a design pattern called newtype that uses tuple structs to strengthen the compiler’s type checking.
Chapter 6: Basic Syntax
The unit of compilation and linking is called a crate.
There are documentation comments that receive special treatment.
Documentation comments include lines beginning with ///, blocks beginning with /***,
lines beginning with //!, and blocks beginning with /*!.
Code with explicit return statements at the end of functions is rare.
The return statement is used only when terminating in the middle of a function.
In Rust, associating a variable with a value is called “binding a variable to a value.”
Right shift is a logical right shift for unsigned integers and an arithmetic right shift for signed integers.
When pattern matching, use | to concatenate patterns and ... to specify ranges.
The if let expression is syntactic sugar for match. The if let expression can be seen as a case with only one pattern.
Chapter 7: Ownership System
A value has exactly one owner. That is, the owner of a value is only one person at any given time. A value is destroyed when its lifetime expires when the last owner exits the scope. If you want to execute some processing at the time of destruction, you can implement a destructor through the Drop trait.
When a struct or enum implements the Copy trait, when you write let p2 = p1,
the value is copied instead of moved. For example, the following types implement the Copy trait:
- Scalar types such as bool, char, i32
- Immutable reference
&Ttype - Function pointers
- Tuple types and array types where all elements implement the Copy trait
The new borrow checker NLL (Non-Lexical Lifetime) can be used when selecting Rust 1.13.0 and 2018 Edition.
Closures implement three traits: Fn, FnMut, and FnOnce, depending on how external variables (free variables) are used from within the closure body.
Chapter 8: Traits and Polymorphism
There are three ways to write trait bounds:
- Write the trait name immediately after the type parameter like
<P: Coordinates> - Write after the function type like
where P: Coordinates - Write in the argument position like
func_name(point: impl Coordinates)
Some standard library traits can be automatically implemented (auto-derived) with the #[derive(XXX)] attribute.
For example, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd can be
auto-derived.
Chapter 9: Creating a Parser
In Rust, parser support libraries are published on crates.io. For example, there are the following two existing parser libraries:
- nom: macro-based parser combinator library
- combine: function-based parser combinator library
Chapter 10: Creating a Package
Let’s organize the terms crate, package, workspace, and project.
- A crate refers to one Rust program. It consists of several modules. As a result of compilation, an executable or library is generated.
- A package is one unit of Cargo. It consists of multiple crates.
- A workspace is a project consisting of multiple packages.
- A project is the maximum unit of Cargo.
Cargo recognizes lib.rs as the entry point for lib crates and main.rs as the entry point for bin crates.
Adding #![deny(missing_docs)] enforces documentation and allows checking with a linter.
When you place tests under tests, Cargo automatically compiles them as test crates.
Documentation blocks in code are also tested, preventing documentation from becoming outdated.
To check test coverage, use tools like kcov or tarpaulin.
What you publish on crates.io cannot be deleted in principle, so upload carefully.
Chapter 11: Web Applications, Database Connection
When using PostgreSQL or MySQL, Diesel provides a safe and extensible ORM and query builder.
Chapter 12: FFI
To call C functions from Rust, describe their signatures in an extern "C" block.
Also, the libc crate covers functions and types defined in C’s libc.
If you place build.rs directly under the project, it will be recognized as a build script.