2023-03-21 00:40:43 +00:00
|
|
|
+++
|
|
|
|
title = "Installing Rust"
|
|
|
|
date = 2023-03-20T17:37:40Z
|
|
|
|
weight = 1
|
|
|
|
+++
|
|
|
|
Since this book is about learning Rust, primarily in a microservices
|
|
|
|
environment, this chapter focuses on installing Rust and describing the tools
|
|
|
|
available to the developer.
|
|
|
|
|
|
|
|
The easiest way to install Rust is to install the [Rustup](https://rustup.rs/)
|
|
|
|
tool. It is one of those blind-trust-in-the-safety-of-the-toolchain things. For
|
|
|
|
Linux and Mac users, the command is a shell script that installs to a user's
|
|
|
|
local account:
|
|
|
|
|
Pre-commit checks and test refactorings.
Re-reading the text, I made a number of changes. The first is that, while it is
nice that Rust allows us to have unit tests in the file whose functionality
we're testing, it's also nice to have the tests somewhere separate, and to have
the tests be a little more modular.
In the `./tests` folder, you can now see the same `health_check` test as the
original, but in an isolated and cleaned-up form. Most importantly, the server
startup code is now in its own function, with a correct return type that
includes a handle to the spawned thread and the address on which that server is
listening; tests can be run in parallel on many different ports and a lot of
code duplication is eliminated.
``` rust
type NullHandle = JoinHandle<()>;
async fn spawn_server() -> (SocketAddr, NullHandle) {
let listener = TcpListener::bind("127.0.0.1:0".parse::<SocketAddr>().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
let handle: NullHandle = tokio::spawn(async move {
axum::Server::from_tcp(listener)
.unwrap()
.serve(app().into_make_service())
.await
.unwrap();
});
(addr, handle)
}
```
It is also possible now to add new tests in a straightforward manner. The
Hyper API is not that much different from the Actix request API, and the Axum
extractors seem to be straightforward. I suspect that what I'm looking at here
with the handle is the idea that, when it goes out of scope, it calls a d
In the introduction I said I was going to be neglecting CI/CD, since I'm a solo
developer. That's true, but I do like my guardrails. I like not being able to
commit garbage to the repository. So I'm going to add some checks, using
[Pre-Commit](https://pre-commit.com/).
Pre-Commit is a Python program, so we'll start by installing it. I'm using a
local Python environment kickstarted with
[Pyenv](https://github.com/pyenv/pyenv).
``` sh
$ pip install pre-commit
```
And inside your project, in the project root, you hook it up with the following commands:
``` sh
$ pre-commit install
$ pre-commit sample-config > .pre-commit-config.yaml
```
I'm going with the default from the rust pre-commit collection, so my
`.pre-commit-config.yaml` file looks like this:
``` yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.1.0
hooks:
- id: check-byte-order-marker
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/pre-commit/pre-commit
rev: v2.5.1
hooks:
- id: validate_manifest
- repo: https://github.com/doublify/pre-commit-rust
rev: master
hooks:
- id: fmt
- id: cargo-check
- id: clippy
```
... and with that, every time I try to commit my code, it will not let me until
these tests pass. And I *like* that level of discipline. This is low-level
validation; it won't catch if I put addition where I meant subtraction, or if I
have a comparison going in the wrong direction, but at least the basics are
handled and, more importantly, the formatting and styling is consistent
throughout all of my code.
2023-03-22 00:52:44 +00:00
|
|
|
``` sh
|
2023-03-21 00:40:43 +00:00
|
|
|
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
|
|
```
|
|
|
|
|
|
|
|
Once installed, you can install Rust itself:
|
|
|
|
|
Pre-commit checks and test refactorings.
Re-reading the text, I made a number of changes. The first is that, while it is
nice that Rust allows us to have unit tests in the file whose functionality
we're testing, it's also nice to have the tests somewhere separate, and to have
the tests be a little more modular.
In the `./tests` folder, you can now see the same `health_check` test as the
original, but in an isolated and cleaned-up form. Most importantly, the server
startup code is now in its own function, with a correct return type that
includes a handle to the spawned thread and the address on which that server is
listening; tests can be run in parallel on many different ports and a lot of
code duplication is eliminated.
``` rust
type NullHandle = JoinHandle<()>;
async fn spawn_server() -> (SocketAddr, NullHandle) {
let listener = TcpListener::bind("127.0.0.1:0".parse::<SocketAddr>().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
let handle: NullHandle = tokio::spawn(async move {
axum::Server::from_tcp(listener)
.unwrap()
.serve(app().into_make_service())
.await
.unwrap();
});
(addr, handle)
}
```
It is also possible now to add new tests in a straightforward manner. The
Hyper API is not that much different from the Actix request API, and the Axum
extractors seem to be straightforward. I suspect that what I'm looking at here
with the handle is the idea that, when it goes out of scope, it calls a d
In the introduction I said I was going to be neglecting CI/CD, since I'm a solo
developer. That's true, but I do like my guardrails. I like not being able to
commit garbage to the repository. So I'm going to add some checks, using
[Pre-Commit](https://pre-commit.com/).
Pre-Commit is a Python program, so we'll start by installing it. I'm using a
local Python environment kickstarted with
[Pyenv](https://github.com/pyenv/pyenv).
``` sh
$ pip install pre-commit
```
And inside your project, in the project root, you hook it up with the following commands:
``` sh
$ pre-commit install
$ pre-commit sample-config > .pre-commit-config.yaml
```
I'm going with the default from the rust pre-commit collection, so my
`.pre-commit-config.yaml` file looks like this:
``` yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.1.0
hooks:
- id: check-byte-order-marker
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/pre-commit/pre-commit
rev: v2.5.1
hooks:
- id: validate_manifest
- repo: https://github.com/doublify/pre-commit-rust
rev: master
hooks:
- id: fmt
- id: cargo-check
- id: clippy
```
... and with that, every time I try to commit my code, it will not let me until
these tests pass. And I *like* that level of discipline. This is low-level
validation; it won't catch if I put addition where I meant subtraction, or if I
have a comparison going in the wrong direction, but at least the basics are
handled and, more importantly, the formatting and styling is consistent
throughout all of my code.
2023-03-22 00:52:44 +00:00
|
|
|
``` sh
|
2023-03-21 00:40:43 +00:00
|
|
|
$ rustup install toolchain stable
|
|
|
|
```
|
|
|
|
|
|
|
|
You should now have Rust compiler and the Rust build and packaging tool, known
|
|
|
|
as Cargo:
|
|
|
|
|
Pre-commit checks and test refactorings.
Re-reading the text, I made a number of changes. The first is that, while it is
nice that Rust allows us to have unit tests in the file whose functionality
we're testing, it's also nice to have the tests somewhere separate, and to have
the tests be a little more modular.
In the `./tests` folder, you can now see the same `health_check` test as the
original, but in an isolated and cleaned-up form. Most importantly, the server
startup code is now in its own function, with a correct return type that
includes a handle to the spawned thread and the address on which that server is
listening; tests can be run in parallel on many different ports and a lot of
code duplication is eliminated.
``` rust
type NullHandle = JoinHandle<()>;
async fn spawn_server() -> (SocketAddr, NullHandle) {
let listener = TcpListener::bind("127.0.0.1:0".parse::<SocketAddr>().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
let handle: NullHandle = tokio::spawn(async move {
axum::Server::from_tcp(listener)
.unwrap()
.serve(app().into_make_service())
.await
.unwrap();
});
(addr, handle)
}
```
It is also possible now to add new tests in a straightforward manner. The
Hyper API is not that much different from the Actix request API, and the Axum
extractors seem to be straightforward. I suspect that what I'm looking at here
with the handle is the idea that, when it goes out of scope, it calls a d
In the introduction I said I was going to be neglecting CI/CD, since I'm a solo
developer. That's true, but I do like my guardrails. I like not being able to
commit garbage to the repository. So I'm going to add some checks, using
[Pre-Commit](https://pre-commit.com/).
Pre-Commit is a Python program, so we'll start by installing it. I'm using a
local Python environment kickstarted with
[Pyenv](https://github.com/pyenv/pyenv).
``` sh
$ pip install pre-commit
```
And inside your project, in the project root, you hook it up with the following commands:
``` sh
$ pre-commit install
$ pre-commit sample-config > .pre-commit-config.yaml
```
I'm going with the default from the rust pre-commit collection, so my
`.pre-commit-config.yaml` file looks like this:
``` yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.1.0
hooks:
- id: check-byte-order-marker
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/pre-commit/pre-commit
rev: v2.5.1
hooks:
- id: validate_manifest
- repo: https://github.com/doublify/pre-commit-rust
rev: master
hooks:
- id: fmt
- id: cargo-check
- id: clippy
```
... and with that, every time I try to commit my code, it will not let me until
these tests pass. And I *like* that level of discipline. This is low-level
validation; it won't catch if I put addition where I meant subtraction, or if I
have a comparison going in the wrong direction, but at least the basics are
handled and, more importantly, the formatting and styling is consistent
throughout all of my code.
2023-03-22 00:52:44 +00:00
|
|
|
``` sh
|
2023-03-21 00:40:43 +00:00
|
|
|
$ rustc --version
|
|
|
|
rustc 1.68.0 (2c8cc3432 2023-03-06)
|
|
|
|
$ cargo --version
|
|
|
|
cargo 1.68.0 (115f34552 2023-02-26)
|
|
|
|
```
|
|
|
|
|
|
|
|
I also installed the following tools:
|
|
|
|
|
Pre-commit checks and test refactorings.
Re-reading the text, I made a number of changes. The first is that, while it is
nice that Rust allows us to have unit tests in the file whose functionality
we're testing, it's also nice to have the tests somewhere separate, and to have
the tests be a little more modular.
In the `./tests` folder, you can now see the same `health_check` test as the
original, but in an isolated and cleaned-up form. Most importantly, the server
startup code is now in its own function, with a correct return type that
includes a handle to the spawned thread and the address on which that server is
listening; tests can be run in parallel on many different ports and a lot of
code duplication is eliminated.
``` rust
type NullHandle = JoinHandle<()>;
async fn spawn_server() -> (SocketAddr, NullHandle) {
let listener = TcpListener::bind("127.0.0.1:0".parse::<SocketAddr>().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
let handle: NullHandle = tokio::spawn(async move {
axum::Server::from_tcp(listener)
.unwrap()
.serve(app().into_make_service())
.await
.unwrap();
});
(addr, handle)
}
```
It is also possible now to add new tests in a straightforward manner. The
Hyper API is not that much different from the Actix request API, and the Axum
extractors seem to be straightforward. I suspect that what I'm looking at here
with the handle is the idea that, when it goes out of scope, it calls a d
In the introduction I said I was going to be neglecting CI/CD, since I'm a solo
developer. That's true, but I do like my guardrails. I like not being able to
commit garbage to the repository. So I'm going to add some checks, using
[Pre-Commit](https://pre-commit.com/).
Pre-Commit is a Python program, so we'll start by installing it. I'm using a
local Python environment kickstarted with
[Pyenv](https://github.com/pyenv/pyenv).
``` sh
$ pip install pre-commit
```
And inside your project, in the project root, you hook it up with the following commands:
``` sh
$ pre-commit install
$ pre-commit sample-config > .pre-commit-config.yaml
```
I'm going with the default from the rust pre-commit collection, so my
`.pre-commit-config.yaml` file looks like this:
``` yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.1.0
hooks:
- id: check-byte-order-marker
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/pre-commit/pre-commit
rev: v2.5.1
hooks:
- id: validate_manifest
- repo: https://github.com/doublify/pre-commit-rust
rev: master
hooks:
- id: fmt
- id: cargo-check
- id: clippy
```
... and with that, every time I try to commit my code, it will not let me until
these tests pass. And I *like* that level of discipline. This is low-level
validation; it won't catch if I put addition where I meant subtraction, or if I
have a comparison going in the wrong direction, but at least the basics are
handled and, more importantly, the formatting and styling is consistent
throughout all of my code.
2023-03-22 00:52:44 +00:00
|
|
|
``` sh
|
|
|
|
$ rustup component add clippy rust-src rust-docs
|
2023-03-21 00:40:43 +00:00
|
|
|
$ cargo install rustfmt rust-analyzer
|
|
|
|
```
|
|
|
|
|
|
|
|
- clippy: A powerful linter that provides useful advice above and beyond the
|
|
|
|
compiler's basic error checking.
|
|
|
|
- rustfmt: A formatting tool that provides a common format for most developers
|
|
|
|
- rust-analyzer: For your IDE, rust-analyzer provides the LSP (Language Server
|
|
|
|
Protocol) for Rust, giving you code completion, on-the-fly error definition,
|
|
|
|
and other luxuries.
|