I’d like to propose new constructor methods on Ipv4Addr and Ipv6Addr to create common addresses. This is intended to be a minor change to resolve a small papercut exposed by the recently added
From implementations on
Specifically, I’d like to add the following to std::net::ip:
Ipv4Addr::localhost() as a method that returns
Ipv4Addr::new(127, 0, 0, 1).
Ipv4Addr::unspecified() as a method that returns
Ipv4Addr::new(0, 0, 0, 0).
Ipv6Addr::localhost() as a method that returns
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).
Ipv6Addr::unspecified() as a method that returns
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).
It has been common practice (in example docs, and quick scripts) to use the
FromStr trait to quickly and readably create socket addresses:
let sockaddr = "127.0.0.1:8000".parse().unwrap(). Rust 1.16 created new From traits on IPAddr, allowing one to infallibly create socket addresses using syntax like
let sockaddr = ([127, 0, 0, 1], 8000).into().
For IPv4 addresses, this new syntax introduces a slight hit to readability, but improves type safety, and removes the superfluous parsing step. For IPv6 addresses, however, they become significantly more awkward to type than the
.parse()d version, due to the lack of zero-elision.
([0, 0, 0, 0, 0, 0, 0, 1], 8000).into(), and
([0, 0, 0, 0, 0, 0, 0, 0], 8000).into().
Having an easy way to infallibly create
SocketAddrs is a clear win, but as a side effect, it makes it more awkward to create common IPv6 addresses than the equivalent legacy IPv4 addresses, which I think is unfortunate for a modern language.
The proposed additions:
IpvAddr::unspecified() make both variants equally simple to create and easily readable without reintroducing fallibility or imposing an unnecessary parsing step.
Creating SocketAddrs using these would be as simple as
let sockaddr = (Ipv6Addr::unspecified(), 8000).into()
I considered as an alternative, a general syntax for infallibly eliding a sequence of zero-segments from Ipv6 addresses. It looked like this:
assert_eq!((, ).into(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
assert_eq!(([0xfe80, 0x540], [0xfabc]).into(), Ipv6Addr::new(0xfe80, 0x540, 0, 0, 0, 0, 0, 0xfabc));
assert_eq!((, ).into(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
I don’t think it is either intuitive what the values represent, or a significant readability win over the existing options. It is also difficult to implement as the two arrays in each tuple can be a number of different sizes. Instead, I opted to solve the problem for the two most commonly used IP address types, which are also the ones that (in IpV6) suffer the most from the lack of zero-segment elision.
unspecified() name is chosen to match the existing
is_unspecified() method. The equivalent test for
localhost() is called
is_loopback(), but tests for a broader category of addresses than the single
localhost() address, so I chose to use a different, but obvious name.
The proposal is implemented in https://github.com/rust-lang/rust/pull/44395