Summary
RFC 1158 proposed the addition
of more functionality for the TcpStream, TcpListener and UdpSocket types,
but was declined so that those APIs could be built up out of tree in the net2
crate. This RFC proposes pulling portions of
net2's APIs into the standard library.
Motivation
The functionality provided by the standard library's wrappers around standard
networking types is fairly limited, and there is a large set of well supported,
standard functionality that is not currently implemented in std::net but has
existed in net2 for some time.
All of the methods to be added map directly to equivalent system calls.
This does not cover the entirety of net2's APIs. In particular, this RFC does
not propose to touch the builder types.
Detailed design
The following methods will be added:
impl TcpStream {
fn set_nodelay(&self, nodelay: bool) -> io::Result<()>;
fn nodelay(&self) -> io::Result<bool>;
fn set_ttl(&self, ttl: u32) -> io::Result<()>;
fn ttl(&self) -> io::Result<u32>;
fn set_only_v6(&self, only_v6: bool) -> io::Result<()>;
fn only_v6(&self) -> io::Result<bool>;
fn take_error(&self) -> io::Result<Option<io::Error>>;
fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()>;
}
impl TcpListener {
fn set_ttl(&self, ttl: u32) -> io::Result<()>;
fn ttl(&self) -> io::Result<u32>;
fn set_only_v6(&self, only_v6: bool) -> io::Result<()>;
fn only_v6(&self) -> io::Result<bool>;
fn take_error(&self) -> io::Result<Option<io::Error>>;
fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()>;
}
impl UdpSocket {
fn set_broadcast(&self, broadcast: bool) -> io::Result<()>;
fn broadcast(&self) -> io::Result<bool>;
fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()>;
fn multicast_loop_v4(&self) -> io::Result<bool>;
fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()>;
fn multicast_ttl_v4(&self) -> io::Result<u32>;
fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()>;
fn multicast_loop_v6(&self) -> io::Result<bool>;
fn set_ttl(&self, ttl: u32) -> io::Result<()>;
fn ttl(&self) -> io::Result<u32>;
fn set_only_v6(&self, only_v6: bool) -> io::Result<()>;
fn only_v6(&self) -> io::Result<bool>;
fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()>;
fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()>;
fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()>;
fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()>;
fn connect<A: ToSocketAddrs>(&self, addr: A) -> Result<()>;
fn send(&self, buf: &[u8]) -> Result<usize>;
fn recv(&self, buf: &mut [u8]) -> Result<usize>;
fn take_error(&self) -> io::Result<Option<io::Error>>;
fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()>;
}
The traditional approach would be to add these as unstable, inherent methods.
However, since inherent methods take precedence over trait methods, this would
cause all code using the extension traits in net2 to start reporting stability
errors. Instead, we have two options:
- Add this functionality as stable inherent methods. The rationale here would
be that time in a nursery crate acts as a de facto stabilization period.
- Add this functionality via unstable extension traits. When/if we decide to
stabilize, we would deprecate the trait and add stable inherent methods.
Extension traits are a bit more annoying to work with, but this would give
us a formal stabilization period.
Option 2 seems like the safer approach unless people feel comfortable with these
APIs.
Drawbacks
This is a fairly significant increase in the surface areas of these APIs, and
most users will never touch some of the more obscure functionality that these
provide.
Alternatives
We can leave some or all of this functionality in net2.
Unresolved questions
The stabilization path (see above).