-
Notifications
You must be signed in to change notification settings - Fork 361
Simplify hello-tokio, move intro article #801
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
joshka
wants to merge
7
commits into
tokio-rs:master
Choose a base branch
from
joshka:jm/tutorial
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
a3bd2db
Shuffle Table of contents
joshka ee90cd0
Bump rust version requirement to 1.70.
joshka 08574b4
Extract async and feature-flags from hello-tokio
joshka b36aeb3
Add troubleshooting section to hello-tokio
joshka 0326c57
Make hero page get started link point at intro
joshka 1342d7b
Fix doc-tests
joshka cc332d5
Update content/tokio/tutorial/hello-tokio.md
joshka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<T: ToSocketAddrs>(addr: T) -> Result<Client> { | ||
| // ... | ||
| # 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"); | ||
| }) | ||
| } | ||
| ``` | ||
|
Comment on lines
+97
to
+134
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to have a link to the documentation of the |
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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: | ||
| <https://docs.rs/tokio/latest/tokio/#feature-flags> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.