Limitations of Standard-Library Networking
Rust's std::net module intentionally follows a minimal, stable design: it exposes thin, portable wrappers around the operating system's synchronous TCP and UDP socket APIs that never break backward compatibility and require zero external dependencies. High-level protocols, cryptography, and asynchronous event loops live in specialized ecosystem crates that can evolve independently of the compiler release cycle.
+-------------------------------------------------------------------+
| Application & Ecosystem Crates |
| HTTP/gRPC (`hyper`, `reqwest`, `axum`, `tonic`) |
| TLS / Cryptography (`rustls`, `tokio-rustls`) |
| Async Runtime & Multiplexing (`tokio`, `mio`, `smol`) |
| Low-Level Socket Options (`socket2`) |
+-------------------------------------------------------------------+
| Rust Standard Library (`std::net`) |
| Synchronous TCP (`TcpStream`, `TcpListener`) & UDP (`UdpSocket`) |
| IP/Socket Addressing (`IpAddr`, `SocketAddr`, `ToSocketAddrs`) |
+-------------------------------------------------------------------+
Key Capabilities Outside std::net
| Capability | Limitation in std::net |
Standard Ecosystem Solution |
|---|---|---|
| Async I/O & Multiplexing | Only synchronous blocking or manual WouldBlock polling; no built-in epoll, kqueue, or IOCP reactor. |
tokio::net, smol::net, or low-level mio |
| TLS / HTTPS Encryption | Raw unencrypted TCP and UDP only; no X.509 certificate validation or TLS handshake. | rustls (pure-Rust TLS) or native-tls |
| HTTP / WebSockets / gRPC | No built-in HTTP/1.1, HTTP/2, HTTP/3, or WebSocket parsers or clients. | reqwest, ureq (sync HTTP), hyper, axum, tonic |
| Advanced Socket Options | Cannot configure SO_REUSEPORT, custom send/receive buffer sizes (SO_RCVBUF/SO_SNDBUF), or bind-before-connect options before calling bind(). |
socket2 crate (converts seamlessly to/from std::net via From/Into) |
| Custom DNS Records | ToSocketAddrs only queries A/AAAA host addresses via blocking getaddrinfo; cannot query MX, TXT, SRV, or DNS-over-HTTPS. |
hickory-resolver |
| Raw / ICMP Packets | Only SOCK_STREAM (TCP) and SOCK_DGRAM (UDP) are exposed; no raw SOCK_RAW or ICMP ping sockets. |
socket2 |
Bridging std::net with the Ecosystem via OwnedFd / OwnedSocket
Even when using external crates like socket2 (to set advanced pre-bind socket options) or tokio (to wrap a pre-bound socket into an async runtime), std::net types act as the universal interoperability currency. They implement From and Into for platform-safe owned descriptors (std::os::fd::OwnedFd on Unix, std::os::windows::io::OwnedSocket on Windows).
use std::io;
use std::net::TcpListener;
fn main() -> io::Result<()> {
let std_listener = TcpListener::bind("127.0.0.1:0")?;
let bound_port = std_listener.local_addr()?.port();
#[cfg(unix)]
{
use std::os::fd::OwnedFd;
// Convert `TcpListener` into an OS-level `OwnedFd` and back without leaking
let raw_owned_fd: OwnedFd = std_listener.into();
let restored_listener = TcpListener::from(raw_owned_fd);
assert_eq!(restored_listener.local_addr()?.port(), bound_port);
println!("Round-tripped TcpListener through OwnedFd on port {bound_port}");
}
#[cfg(windows)]
{
use std::os::windows::io::OwnedSocket;
let raw_owned_sock: OwnedSocket = std_listener.into();
let restored_listener = TcpListener::from(raw_owned_sock);
assert_eq!(restored_listener.local_addr()?.port(), bound_port);
println!("Round-tripped TcpListener through OwnedSocket on port {bound_port}");
}
Ok(())
}
Round-tripped TcpListener through OwnedFd on port 54322
Choosing Between std::net and Async Networking
Use std::net directly when:
- Writing CLI utilities, local IPC daemons, embedded test harnesses, or services with a bounded number of concurrent connections where a thread-per-connection or small thread pool is sufficient.
- Minimizing compile times, binary size, and third-party dependency audits.
Use tokio::net / ecosystem crates when:
- Handling thousands of concurrent long-lived connections (such as WebSockets, reverse proxies, or public HTTP API servers).
- Requiring TLS encryption (
rustls), HTTP/2 multiplexing (hyper), or cancellable timeouts across composite I/O workflows (tokio::time::timeout).