Cargo dependencies
Feel free to skip this section, and just use features = ["full"]
while
learning. This section provides some examples of how and when to choose
specific feature groups
in Cargo.toml
.
Often using all features is fine
When writing an app, we’ll often use all of Tokio’s features to accelerate our development time.
Cargo.toml
:
[dependencies]
tokio = { version = "0.2", features = ["full"] }
For most apps, the additional compile time is hardly noticeable relative to the cognitive overhead of thinking about which features are needed.
Selecting features
When we develop protocol implementations that are used by client apps and servers, we want to limit the dependencies to decrease compile time – not just for us, but also for the users of our crates.
Let’s take a look at our tiny client app and see what features are needed.
tokio main
If we replace our src/main.rs
with the following code (only adding the
#[tokio::main]
attribute to the default hello world generated by cargo new
):
main.rs
:
#[tokio::main]
async fn main() {
println!("doing nothing yet");
}
Then we can choose whether to use threads (commonly used for servers):
tokio = { version = "0.2", features = ["macros", "rt-threaded"] }
or lightweight cooperative multi-tasking (often required for low-profile clients):
tokio = { version = "0.2", features = ["macros", "rt-core"] }
TcpStream connect
As we start to build the app, we’ll need more features. The line of code
where we call TcpStream::connect
requires two features:
# #![deny(deprecated)]
# #![allow(unused_mut)]
# #![allow(unused_variables)]
# use tokio::net::TcpStream;
# #[tokio::main]
# async fn main() {
let mut stream = TcpStream::connect("127.0.0.1:6142").await.unwrap();
# }
dns
for converting the string127.0.0.1:6142
to an IP address – Tokio will need to do a DNS lookup just in case.tcp
for handling the TCP connection
Since our example uses a specific ip address, we can reduce dependencies
further by creating a std::net::SocketAddr
:
# #![deny(deprecated)]
# #![allow(unused_mut)]
# #![allow(unused_variables)]
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use tokio::net::TcpStream;
#[tokio::main]
async fn main() {
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6142);
let mut stream = TcpStream::connect(addr).await.unwrap();
}
Writing to the socket
The write
method is defined in tokio::io::AsyncWriteExt
which requires
io-util
.
# #![deny(deprecated)]
# #![allow(unused_variables)]
# use tokio::net::TcpStream;
# use tokio::prelude::*;
# #[tokio::main]
# async fn main() {
# let mut stream = TcpStream::connect("127.0.0.1:6142").await.unwrap();
let result = stream.write(b"hello world\n").await;
# }
The write
method in our example is declared with use tokio::prelude::*
.
The tokio prelude
follows the Rust prelude pattern
to make using
multiple types more convenient. We could be more explicit by declaring use tokio::io::AsyncWriteExt
.
Learn more about features
The API reference
indicates which features are needed for different APIs.
If anything missing, please open an issue
and one of the Tokio elves
will figure out where it is declared and will fix it asap!