Rust does not allow function overloading or variadic functions, so a few different options are available to handle different use cases. I will not go into depth about all the options, but I want to show off one of option that I didn’t see in the blog posts I found but instead discovered while working with TcpStreams.

The signature for connect() is pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<Self> and it can be called with a variety of arguments, without any explicit conversion:

let stream = TcpStream::connect("127.0.0.1:8080")
// or
let stream = TcpStream::connect([[127, 0, 0, 1], 8080])
// or
let stream = TcpStream::connect((Ipv4Addr::new(127, 0, 0, 1), 8080));

This works by implementing the trait ToSocketAddrs for each of the object types1:

The trait just enforces the existence of the conversion function

pub trait ToSocketAddrs {
    /// Converts this object to an iterator of resolved [`SocketAddr`]s.
    //...
    fn to_socket_addrs(&self) -> io::Result<Self::Iter>;
}

The implementations end up being quite short in most cases2:

impl ToSocketAddrs for SocketAddr {
    // ...
}

impl ToSocketAddrs for (String, u16) {
    type Iter = vec::IntoIter<SocketAddr>;
    fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
        (&*self.0, self.1).to_socket_addrs()
    }
}