diff --git a/components/hero.tsx b/components/hero.tsx index 9d1db320..47b9fcde 100644 --- a/components/hero.tsx +++ b/components/hero.tsx @@ -1,6 +1,6 @@ import React, { FC } from "react"; -const gettingStarted = "/tokio/tutorial"; +const gettingStarted = "/tokio/introduction"; const icons = [ "bytes", diff --git a/content/tokio/introduction.md b/content/tokio/introduction.md new file mode 100644 index 00000000..071baae9 --- /dev/null +++ b/content/tokio/introduction.md @@ -0,0 +1,120 @@ +--- +title: "Introduction" +subtitle: "Overview" +--- + +Tokio is an asynchronous runtime for the Rust programming language. It provides +the building blocks needed for writing networking applications. It gives the +flexibility to target a wide range of systems, from large servers with dozens of +cores to small embedded devices. + +At a high level, Tokio provides a few major components: + +- A multi-threaded runtime for executing asynchronous code. +- An asynchronous version of the standard library. +- A large ecosystem of libraries. + +# Tokio's role in your project + +When you write your application in an asynchronous manner, you enable it to +scale much better by reducing the cost of doing many things at the same time. +However, asynchronous Rust code does not run on its own, so you must choose a +runtime to execute it. The Tokio library is the most widely used runtime, +surpassing all other runtimes in usage combined. + +Additionally, Tokio provides many useful utilities. When writing asynchronous +code, you cannot use the ordinary blocking APIs provided by the Rust standard +library, and must instead use asynchronous versions of them. These alternate +versions are provided by Tokio, mirroring the API of the Rust standard library +where it makes sense. + +# Advantages of Tokio + +This section will outline some advantages of Tokio. + +## Fast + +Tokio is _fast_, built on top of the Rust programming language, which itself is +fast. This is done in the spirit of Rust with the goal that you should not be +able to improve the performance by writing equivalent code by hand. + +Tokio is _scalable_, built on top of the async/await language feature, which +itself is scalable. When dealing with networking, there's a limit to how fast +you can handle a connection due to latency, so the only way to scale is to +handle many connections at once. With the async/await language feature, +increasing the number of concurrent operations becomes incredibly cheap, +allowing you to scale to a large number of concurrent tasks. + +## Reliable + +Tokio is built using Rust, which is a language that empowers everyone +to build reliable and efficient software. A [number][microsoft] of +[studies][chrome] have found that roughly ~70% of high severity security bugs +are the result of memory unsafety. Using Rust eliminates this entire class of +bugs in your applications. + +Tokio also focuses heavily on providing consistent behaviour with no surprises. +Tokio's major goal is to allow users to deploy predictable software that will +perform the same day in and day out with reliable response times and no +unpredictable latency spikes. + +[microsoft]: https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/ +[chrome]: https://www.chromium.org/Home/chromium-security/memory-safety + +## Easy + +With Rust's async/await feature, the complexity of writing asynchronous +applications has been lowered substantially. Paired with Tokio's utilities and +vibrant ecosystem, writing applications is a breeze. + +Tokio follows the standard library's naming convention when it makes sense. This +allows easily converting code written with only the standard library to code +written with Tokio. With the strong type system of Rust, the ability to deliver +correct code easily is unparalleled. + +## Flexible + +Tokio provides multiple variations of the runtime. Everything from a +multi-threaded, [work-stealing] runtime to a light-weight, single-threaded +runtime. Each of these runtimes come with many knobs to allow users to tune them +to their needs. + +[work-stealing]: https://en.wikipedia.org/wiki/Work_stealing + +# When not to use Tokio + +Although Tokio is useful for many projects that need to do a lot of things +simultaneously, there are also some use-cases where Tokio is not a good fit. + +- Speeding up CPU-bound computations by running them in parallel on several + threads. Tokio is designed for IO-bound applications where each individual + task spends most of its time waiting for IO. If the only thing your + application does is run computations in parallel, you should be using + [rayon]. That said, it is still possible to "mix & match" + if you need to do both. See [this blog post for a practical example][rayon-example]. +- Reading a lot of files. Although it seems like Tokio would be useful for + projects that simply need to read a lot of files, Tokio provides no advantage + here compared to an ordinary threadpool. This is because operating systems + generally do not provide asynchronous file APIs. +- Sending a single web request. The place where Tokio gives you an advantage is + when you need to do many things at the same time. If you need to use a + library intended for asynchronous Rust such as [reqwest], but you don't need + to do a lot of things at once, you should prefer the blocking version of that + library, as it will make your project simpler. Using Tokio will still work, + of course, but provides no real advantage over the blocking API. If the + library doesn't provide a blocking API, see [the chapter on + bridging with sync code][bridging]. + +[rayon]: https://docs.rs/rayon/ +[rayon-example]: https://ryhl.io/blog/async-what-is-blocking/#the-rayon-crate +[reqwest]: https://docs.rs/reqwest/ +[bridging]: /tokio/topics/bridging + +# Getting Help + +At any point, if you get stuck, you can always get help on [Discord] or [GitHub +discussions][disc]. Don't worry about asking "beginner" questions. We all start +somewhere and are happy to help. + +[discord]: https://discord.gg/tokio +[disc]: https://github.com/tokio-rs/tokio/discussions diff --git a/content/tokio/topics/async.md b/content/tokio/topics/async.md new file mode 100644 index 00000000..5b868c36 --- /dev/null +++ b/content/tokio/topics/async.md @@ -0,0 +1,134 @@ +--- +title: Asynchronous Programming +--- + +# What is Asynchronous Programming? + +Most computer programs are executed in the same order in which they are written. +The first line executes, then the next, and so on. With synchronous programming, +when a program encounters an operation that cannot be completed immediately, it +will block until the operation completes. For example, establishing a TCP +connection requires an exchange with a peer over the network, which can take a +sizeable amount of time. During this time, the thread is blocked. + +With asynchronous programming, operations that cannot complete immediately are +suspended to the background. The thread is not blocked, and can continue running +other things. Once the operation completes, the task is unsuspended and continues +processing from where it left off. Our example from before only has one task, so +nothing happens while it is suspended, but asynchronous programs typically have +many such tasks. + +Although asynchronous programming can result in faster applications, it often +results in much more complicated programs. The programmer is required to track +all the state necessary to resume work once the asynchronous operation +completes. Historically, this is a tedious and error-prone task. + +# Asynchronous Functions + +Rust implements asynchronous programming using a feature called [`async/await`]. +Functions that perform asynchronous operations are labeled with the `async` +keyword. In the tutorial example, we used the `mini_redis::connect` function. It +is defined like this: + +```rust +use mini_redis::Result; +use mini_redis::client::Client; +use tokio::net::ToSocketAddrs; + +pub async fn connect(addr: T) -> Result { + // ... +# unimplemented!() +} +``` + +The `async fn` definition looks like a regular synchronous function, but +operates asynchronously. Rust transforms the `async fn` at **compile** time into +a routine that operates asynchronously. Any calls to `.await` within the `async +fn` yield control back to the thread. The thread may do other work while the +operation processes in the background. + +> **warning** +> Although other languages implement [`async/await`] too, Rust takes a unique +> approach. Primarily, Rust's async operations are **lazy**. This results in +> different runtime semantics than other languages. + +[`async/await`]: https://en.wikipedia.org/wiki/Async/await + +# Using `async/await` + +Async functions are called like any other Rust function. However, calling these +functions does not result in the function body executing. Instead, calling an +`async fn` returns a value representing the operation. This is conceptually +analogous to a zero-argument closure. To actually run the operation, you should +use the `.await` operator on the return value. + +For example, the given program + +```rust +async fn say_world() { + println!("world"); +} + +#[tokio::main] +async fn main() { + // Calling `say_world()` does not execute the body of `say_world()`. + let op = say_world(); + + // This println! comes first + println!("hello"); + + // Calling `.await` on `op` starts executing `say_world`. + op.await; +} +``` + +outputs: + +```text +hello +world +``` + +The return value of an `async fn` is an anonymous type that implements the +[`Future`] trait. + +[`Future`]: https://doc.rust-lang.org/std/future/trait.Future.html + +# Async `main` function + +The main function used to launch the application differs from the usual one +found in most of Rust's crates. + +1. It is an `async fn` +2. It is annotated with `#[tokio::main]` + +An `async fn` is used as we want to enter an asynchronous context. However, +asynchronous functions must be executed by a [runtime]. The runtime contains the +asynchronous task scheduler, provides evented I/O, timers, etc. The runtime does +not automatically start, so the main function needs to start it. + +[runtime]: https://docs.rs/tokio/1/tokio/runtime/index.html + +The `#[tokio::main]` function is a macro. It transforms the `async fn main()` +into a synchronous `fn main()` that initializes a runtime instance and executes +the async main function. + +For example, the following: + +```rust +#[tokio::main] +async fn main() { + println!("hello"); +} +``` + +gets transformed into: + +```rust +fn main() { + let mut rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async { + println!("hello"); + }) +} +``` diff --git a/content/tokio/topics/feature-flags.md b/content/tokio/topics/feature-flags.md new file mode 100644 index 00000000..932403dd --- /dev/null +++ b/content/tokio/topics/feature-flags.md @@ -0,0 +1,17 @@ +--- +title: Cargo Feature Flags +--- + +When depending on Tokio for the tutorial, the `full` feature flag was enabled: + +```toml +tokio = { version = "1", features = ["full"] } +``` + +Tokio has a lot of functionality (TCP, UDP, Unix sockets, timers, sync utilities, multiple scheduler +types, etc). Not all applications need all functionality. When attempting to optimize compile time +or the end application footprint, the application can decide to opt into **only** the features it +uses. + +More information about the available flags is available in the API docs at: + diff --git a/content/tokio/tutorial/hello-tokio.md b/content/tokio/tutorial/hello-tokio.md index 72ff89e1..8c3b6a60 100644 --- a/content/tokio/tutorial/hello-tokio.md +++ b/content/tokio/tutorial/hello-tokio.md @@ -13,19 +13,36 @@ then read back the key. This will be done using the Mini-Redis client library. Let's start by generating a new Rust app: ```bash -$ cargo new my-redis -$ cd my-redis +cargo new my-redis +cd my-redis ``` ## Add dependencies -Next, open `Cargo.toml` and add the following right below `[dependencies]`: +Next, add [tokio] and [mini-redis] to the `[dependencies]` section in the +`Cargo.toml` manifest: + +[tokio]: https://crates.io/crates/tokio +[mini-redis]: https://crates.io/crates/mini-redis + +```bash +cargo add tokio --features full +cargo add mini-redis +``` + +This will result in something similar to the following (the versions may be +later than this): ```toml -tokio = { version = "1", features = ["full"] } -mini-redis = "0.4" +[dependencies] +tokio = { version = "1.41.1", features = ["full"] } +mini-redis = "0.4.1" ``` +We use the `full` feature flag for simplicity. For more +information about the available feature flags see the [feature flags +topic](/tokio/topics/feature-flags). + ## Write the code Then, open `main.rs` and replace the contents of the file with: @@ -45,23 +62,25 @@ async fn main() -> Result<()> { // Get key "hello" let result = client.get("hello").await?; - println!("got value from the server; result={:?}", result); + println!("got value from the server; result={result:?}"); Ok(()) } # } ``` +## Run the code + Make sure the Mini-Redis server is running. In a separate terminal window, run: ```bash -$ mini-redis-server +mini-redis-server ``` If you have not already installed mini-redis, you can do so with ```bash -$ cargo install mini-redis +cargo install mini-redis ``` Now, run the `my-redis` application: @@ -77,11 +96,55 @@ You can find the full code [here][full]. [full]: https://github.com/tokio-rs/website/blob/master/tutorial-code/hello-tokio/src/main.rs +# Troubleshooting + +A common mistake is to forget to add `.await` on calls to asynchronous +functions. This is particularly easy to forget for functions where the return +value is not used by the application. The compiler warns you and teaches you how +to fix this error. + +```plain +warning: unused implementer of `std::future::Future` that must be used + --> examples/hello-redis.rs:12:5 + | +12 | client.set("hello", "world".into()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: futures do nothing unless you `.await` or poll them + = note: `#[warn(unused_must_use)]` on by default +``` + # Breaking it down Let's take some time to go over what we just did. There isn't much code, but a lot is happening. +## Async `main` function + +```rust +# use mini_redis::{Result}; +#[tokio::main] +async fn main() -> Result<()> { + // ... + + Ok(()) +} +``` + +The main function is an asynchronous function. This is indicated by the `async` +keyword before the function definition. It returns `mini_redis::Result`. The +`Ok(())` value indicates that the program completed successfully. The +`#[tokio::main]` macro, wraps the asynchronous function in a standard +synchronous function and runs the asynchronous code on the tokio runtime. + +For more information on this see the [Async `main` function] section of the +[Asynchronous Programming] topic. + +[Async `main` function]: /tokio/topics/async#async-main-function +[Asynchronous Programming]: /tokio/topics/async + +## Connecting to redis + ```rust # use mini_redis::client; # async fn dox() -> mini_redis::Result<()> { @@ -94,155 +157,58 @@ The [`client::connect`] function is provided by the `mini-redis` crate. It asynchronously establishes a TCP connection with the specified remote address. Once the connection is established, a `client` handle is returned. Even though the operation is performed asynchronously, the code we write **looks** -synchronous. The only indication that the operation is asynchronous is the -`.await` operator. - -[`client::connect`]: https://docs.rs/mini-redis/0.4/mini_redis/client/fn.connect.html +synchronous. To actually run the connect operation, on the runtime, you need to +add the `await` keyword. -## What is asynchronous programming? +We use the `?` operator on this call as the method may fail if the underlying +connection cannot be established (e.g. if the server is not running). -Most computer programs are executed in the same order in which they are written. -The first line executes, then the next, and so on. With synchronous programming, -when a program encounters an operation that cannot be completed immediately, it -will block until the operation completes. For example, establishing a TCP -connection requires an exchange with a peer over the network, which can take a -sizeable amount of time. During this time, the thread is blocked. +[`client::connect`]: https://docs.rs/mini-redis/latest/mini_redis/client/fn.connect.html -With asynchronous programming, operations that cannot complete immediately are -suspended to the background. The thread is not blocked, and can continue running -other things. Once the operation completes, the task is unsuspended and continues -processing from where it left off. Our example from before only has one task, so -nothing happens while it is suspended, but asynchronous programs typically have -many such tasks. +## Setting and getting a key -Although asynchronous programming can result in faster applications, it often -results in much more complicated programs. The programmer is required to track -all the state necessary to resume work once the asynchronous operation -completes. Historically, this is a tedious and error-prone task. +To set a key, we use the [`Client::set`] method, passing the key and value. This +method is asynchronous, so we use `await`. -## Compile-time green-threading - -Rust implements asynchronous programming using a feature called [`async/await`]. -Functions that perform asynchronous operations are labeled with the `async` -keyword. In our example, the `connect` function is defined like this: +[`Client::set`]: https://docs.rs/mini-redis/latest/mini_redis/client/struct.Client.html#method.set ```rust -use mini_redis::Result; -use mini_redis::client::Client; -use tokio::net::ToSocketAddrs; - -pub async fn connect(addr: T) -> Result { - // ... -# unimplemented!() -} -``` - -The `async fn` definition looks like a regular synchronous function, but -operates asynchronously. Rust transforms the `async fn` at **compile** time into -a routine that operates asynchronously. Any calls to `.await` within the `async -fn` yield control back to the thread. The thread may do other work while the -operation processes in the background. - -> **warning** -> Although other languages implement [`async/await`] too, Rust takes a unique -> approach. Primarily, Rust's async operations are **lazy**. This results in -> different runtime semantics than other languages. - -[`async/await`]: https://en.wikipedia.org/wiki/Async/await - -If this doesn't quite make sense yet, don't worry. We will explore `async/await` -more throughout the guide. - -## Using `async/await` - -Async functions are called like any other Rust function. However, calling these -functions does not result in the function body executing. Instead, calling an -`async fn` returns a value representing the operation. This is conceptually -analogous to a zero-argument closure. To actually run the operation, you should -use the `.await` operator on the return value. - -For example, the given program - -```rust -async fn say_world() { - println!("world"); -} - -#[tokio::main] -async fn main() { - // Calling `say_world()` does not execute the body of `say_world()`. - let op = say_world(); - - // This println! comes first - println!("hello"); - - // Calling `.await` on `op` starts executing `say_world`. - op.await; -} -``` - -outputs: - -```text -hello -world +# use mini_redis::client; +# async fn dox() -> mini_redis::Result<()> { +# let mut client = client::connect("127.0.0.1:6379").await?; +client.set("hello", "world".into()).await?; +# Ok(()) +# } ``` -The return value of an `async fn` is an anonymous type that implements the -[`Future`] trait. +To get the value of a key, we use the [`Client::get`] method, which also requires `await`. -[`Future`]: https://doc.rust-lang.org/std/future/trait.Future.html - -## Async `main` function - -The main function used to launch the application differs from the usual one -found in most of Rust's crates. - -1. It is an `async fn` -2. It is annotated with `#[tokio::main]` - -An `async fn` is used as we want to enter an asynchronous context. However, -asynchronous functions must be executed by a [runtime]. The runtime contains the -asynchronous task scheduler, provides evented I/O, timers, etc. The runtime does -not automatically start, so the main function needs to start it. - -The `#[tokio::main]` function is a macro. It transforms the `async fn main()` -into a synchronous `fn main()` that initializes a runtime instance and executes -the async main function. - -For example, the following: +[`Client::get`]: https://docs.rs/mini-redis/latest/mini_redis/client/struct.Client.html#method.get ```rust -#[tokio::main] -async fn main() { - println!("hello"); -} +# use mini_redis::client; +# async fn dox() -> mini_redis::Result<()> { +# let mut client = client::connect("127.0.0.1:6379").await?; +let result = client.get("hello").await?; +# Ok(()) +# } ``` -gets transformed into: +The result is an `Option`, which will be `Some(Bytes)` if the key exists +or `None` if it does not. We print the Debug format of the result. -```rust -fn main() { - let mut rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(async { - println!("hello"); - }) -} +```rust,ignore +# let result = 42; +println!("got value from the server; result={result:?}"); ``` -The details of the Tokio runtime will be covered later. +# Conclusion -[runtime]: https://docs.rs/tokio/1/tokio/runtime/index.html +Congratulations on writing your first Tokio application! In the next section, +we will explore Tokio's asynchronous programming model in more detail. -## Cargo features - -When depending on Tokio for this tutorial, the `full` feature flag is enabled: - -```toml -tokio = { version = "1", features = ["full"] } -``` +If you have any questions, feel free to ask on the [Tokio Discord server] or +[GitHub Discussions]. -Tokio has a lot of functionality (TCP, UDP, Unix sockets, timers, sync -utilities, multiple scheduler types, etc). Not all applications need all -functionality. When attempting to optimize compile time or the end application -footprint, the application can decide to opt into **only** the features it uses. +[Tokio Discord server]: https://discord.gg/tokio +[GitHub Discussions]: https://github.com/tokio-rs/tokio/discussions diff --git a/content/tokio/tutorial/index.md b/content/tokio/tutorial/index.md index 36898235..28ea3152 100644 --- a/content/tokio/tutorial/index.md +++ b/content/tokio/tutorial/index.md @@ -1,114 +1,23 @@ --- title: "Tutorial" -subtitle: "Overview" +subtitle: "Setup" --- -Tokio is an asynchronous runtime for the Rust programming language. It provides -the building blocks needed for writing networking applications. It gives the -flexibility to target a wide range of systems, from large servers with dozens of -cores to small embedded devices. - -At a high level, Tokio provides a few major components: - - - A multi-threaded runtime for executing asynchronous code. - - An asynchronous version of the standard library. - - A large ecosystem of libraries. - -# Tokio's role in your project - -When you write your application in an asynchronous manner, you enable it to -scale much better by reducing the cost of doing many things at the same time. -However, asynchronous Rust code does not run on its own, so you must choose a -runtime to execute it. The Tokio library is the most widely used runtime, -surpassing all other runtimes in usage combined. - -Additionally, Tokio provides many useful utilities. When writing asynchronous -code, you cannot use the ordinary blocking APIs provided by the Rust standard -library, and must instead use asynchronous versions of them. These alternate -versions are provided by Tokio, mirroring the API of the Rust standard library -where it makes sense. - -# Advantages of Tokio - -This section will outline some advantages of Tokio. - -## Fast - -Tokio is _fast_, built on top of the Rust programming language, which itself is -fast. This is done in the spirit of Rust with the goal that you should not be -able to improve the performance by writing equivalent code by hand. - -Tokio is _scalable_, built on top of the async/await language feature, which -itself is scalable. When dealing with networking, there's a limit to how fast -you can handle a connection due to latency, so the only way to scale is to -handle many connections at once. With the async/await language feature, -increasing the number of concurrent operations becomes incredibly cheap, -allowing you to scale to a large number of concurrent tasks. - -## Reliable - -Tokio is built using Rust, which is a language that empowers everyone -to build reliable and efficient software. A [number][microsoft] of -[studies][chrome] have found that roughly ~70% of high severity security bugs -are the result of memory unsafety. Using Rust eliminates this entire class of -bugs in your applications. - -Tokio also focuses heavily on providing consistent behaviour with no surprises. -Tokio's major goal is to allow users to deploy predictable software that will -perform the same day in and day out with reliable response times and no -unpredictable latency spikes. - -[microsoft]: https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/ -[chrome]: https://www.chromium.org/Home/chromium-security/memory-safety - -## Easy - -With Rust's async/await feature, the complexity of writing asynchronous -applications has been lowered substantially. Paired with Tokio's utilities and -vibrant ecosystem, writing applications is a breeze. - -Tokio follows the standard library's naming convention when it makes sense. This -allows easily converting code written with only the standard library to code -written with Tokio. With the strong type system of Rust, the ability to deliver -correct code easily is unparalleled. - -## Flexible - -Tokio provides multiple variations of the runtime. Everything from a -multi-threaded, [work-stealing] runtime to a light-weight, single-threaded -runtime. Each of these runtimes come with many knobs to allow users to tune them -to their needs. - -[work-stealing]: https://en.wikipedia.org/wiki/Work_stealing - -# When not to use Tokio - -Although Tokio is useful for many projects that need to do a lot of things -simultaneously, there are also some use-cases where Tokio is not a good fit. - - - Speeding up CPU-bound computations by running them in parallel on several - threads. Tokio is designed for IO-bound applications where each individual - task spends most of its time waiting for IO. If the only thing your - application does is run computations in parallel, you should be using - [rayon]. That said, it is still possible to "mix & match" - if you need to do both. See [this blog post for a practical example][rayon-example]. - - Reading a lot of files. Although it seems like Tokio would be useful for - projects that simply need to read a lot of files, Tokio provides no advantage - here compared to an ordinary threadpool. This is because operating systems - generally do not provide asynchronous file APIs. - - Sending a single web request. The place where Tokio gives you an advantage is - when you need to do many things at the same time. If you need to use a - library intended for asynchronous Rust such as [reqwest], but you don't need - to do a lot of things at once, you should prefer the blocking version of that - library, as it will make your project simpler. Using Tokio will still work, - of course, but provides no real advantage over the blocking API. If the - library doesn't provide a blocking API, see [the chapter on - bridging with sync code][bridging]. - -[rayon]: https://docs.rs/rayon/ -[rayon-example]: https://ryhl.io/blog/async-what-is-blocking/#the-rayon-crate -[reqwest]: https://docs.rs/reqwest/ -[bridging]: /tokio/topics/bridging +This tutorial will take you step by step through the process of building a +[Redis] client and server. We will start with the basics of asynchronous +programming with Rust and build up from there. We will implement a subset of +Redis commands but will get a comprehensive tour of Tokio. + +# Mini-Redis + +The project that you will build in this tutorial is available as [Mini-Redis on +GitHub][mini-redis]. Mini-Redis is designed with the primary goal of learning +Tokio, and is therefore very well commented, but this also means that Mini-Redis +is missing some features you would want in a real Redis library. You can find +production-ready Redis libraries on [crates.io](https://crates.io/). + +We will use Mini-Redis directly in the tutorial. This allows us to use parts of +Mini-Redis in the tutorial before we implement them later in the tutorial. # Getting Help @@ -118,3 +27,67 @@ somewhere and are happy to help. [discord]: https://discord.gg/tokio [disc]: https://github.com/tokio-rs/tokio/discussions + +# Prerequisites + +Readers should already be familiar with [Rust]. The [Rust book][book] is an +excellent resource to get started with. + +While not required, some experience with writing networking code using the [Rust +standard library][std] or another language can be helpful. + +No pre-existing knowledge of Redis is required. + +[rust]: https://rust-lang.org +[book]: https://doc.rust-lang.org/book/ +[std]: https://doc.rust-lang.org/std/ + +## Rust + +Before getting started, you should make sure that you have the +[Rust][install-rust] toolchain installed and ready to go. If you don't have it, +the easiest way to install it is using [rustup]. + +This tutorial requires a minimum of Rust version `1.70.0`, but the most +recent stable version of Rust is recommended. + +To check that Rust is installed on your computer, run the following: + +```bash +rustc --version +``` + +You should see output like `rustc 1.81.0 (eeb90cda1 2024-09-04)`. + +## Mini-Redis server + +Next, install the Mini-Redis server. This will be used to test our client as we +build it. + +```bash +cargo install mini-redis +``` + +Make sure that it was successfully installed by starting the server: + +```bash +mini-redis-server +``` + +Then, in a separate terminal window, try to get the key `foo` using `mini-redis-cli` + +```bash +mini-redis-cli get foo +``` + +You should see `(nil)`. + +# Ready to go + +That's it, everything is ready to go. Go to the next page to write your first +asynchronous Rust application. + +[redis]: https://redis.io +[mini-redis]: https://github.com/tokio-rs/mini-redis +[install-rust]: https://www.rust-lang.org/tools/install +[rustup]: https://rustup.rs/ diff --git a/content/tokio/tutorial/setup.md b/content/tokio/tutorial/setup.md deleted file mode 100644 index 437e4e4e..00000000 --- a/content/tokio/tutorial/setup.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: "Setup" ---- - -This tutorial will take you step by step through the process of building a -[Redis] client and server. We will start with the basics of asynchronous -programming with Rust and build up from there. We will implement a subset of -Redis commands but will get a comprehensive tour of Tokio. - -# Mini-Redis - -The project that you will build in this tutorial is available as [Mini-Redis on -GitHub][mini-redis]. Mini-Redis is designed with the primary goal of learning -Tokio, and is therefore very well commented, but this also means that Mini-Redis -is missing some features you would want in a real Redis library. You can find -production-ready Redis libraries on [crates.io](https://crates.io/). - -We will use Mini-Redis directly in the tutorial. This allows us to use parts of -Mini-Redis in the tutorial before we implement them later in the tutorial. - -# Getting Help - -At any point, if you get stuck, you can always get help on [Discord] or [GitHub -discussions][disc]. Don't worry about asking "beginner" questions. We all start -somewhere and are happy to help. - -[discord]: https://discord.gg/tokio -[disc]: https://github.com/tokio-rs/tokio/discussions - -# Prerequisites - -Readers should already be familiar with [Rust]. The [Rust book][book] is an -excellent resource to get started with. - -While not required, some experience with writing networking code using the [Rust -standard library][std] or another language can be helpful. - -No pre-existing knowledge of Redis is required. - -[rust]: https://rust-lang.org -[book]: https://doc.rust-lang.org/book/ -[std]: https://doc.rust-lang.org/std/ - -## Rust - -Before getting started, you should make sure that you have the -[Rust][install-rust] toolchain installed and ready to go. If you don't have it, -the easiest way to install it is using [rustup]. - -This tutorial requires a minimum of Rust version `1.45.0`, but the most -recent stable version of Rust is recommended. - -To check that Rust is installed on your computer, run the following: - -```bash -$ rustc --version -``` - -You should see output like `rustc 1.46.0 (04488afe3 2020-08-24)`. - -## Mini-Redis server - -Next, install the Mini-Redis server. This will be used to test our client as we -build it. - -```bash -$ cargo install mini-redis -``` - -Make sure that it was successfully installed by starting the server: - -```bash -$ mini-redis-server -``` - -Then, in a separate terminal window, try to get the key `foo` using `mini-redis-cli` - -```bash -$ mini-redis-cli get foo -``` - -You should see `(nil)`. - -# Ready to go - -That's it, everything is ready to go. Go to the next page to write your first -asynchronous Rust application. - -[redis]: https://redis.io -[mini-redis]: https://github.com/tokio-rs/mini-redis -[install-rust]: https://www.rust-lang.org/tools/install -[rustup]: https://rustup.rs/ diff --git a/netlify.toml b/netlify.toml index cf5d72b3..b10fb130 100644 --- a/netlify.toml +++ b/netlify.toml @@ -29,6 +29,11 @@ NETLIFY_NEXT_PLUGIN_SKIP = "true" from = "/docs/overview" to = "/tokio/tutorial" +# Previous setup steps were moved to the main tutorial page +[[redirects]] +from = "/tokio/tutorial/setup" +to = "/tokio/tutorial" + # Redirect previous rss feed location [[redirects]] from = "/_next/static/feed.xml" diff --git a/pages/[...slug].tsx b/pages/[...slug].tsx index f40c54f8..327888fa 100644 --- a/pages/[...slug].tsx +++ b/pages/[...slug].tsx @@ -5,9 +5,9 @@ const menu = { tokio: { title: "Tokio", nested: { + introduction: {}, tutorial: { nested: [ - "setup", "hello-tokio", "spawning", "shared-state", @@ -21,6 +21,8 @@ const menu = { }, topics: { nested: [ + "async", + "feature-flags", "bridging", "shutdown", "tracing",