Networking with the Standard Library

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).