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();
# }
dnsfor converting the string127.0.0.1:6142to an IP address – Tokio will need to do a DNS lookup just in case.tcpfor 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!