Handle TcpListener::bind()'s Result without unwrap

I am completely new to Rust. When attempting to create a client/ server over tcp listener, I first create a function that listen on a specific address port. The code looks like

    let listener = TcpListener
        ::bind(format!("{address}:{port}"))
        .unwrap();
    listener.set_nonblocking(true);
    for stream in listener.incoming() { ... }

But it looks like the method unwrap() is discouraged. But many examples I found online, including official doc, employ unwrap(). If it's not recommended, what's a better way to accomplish such task?

I tried something like following, but I do not know how to iterate over the stream.

    let result: Result<Result<(), Error>, Error> = TcpListener
        ::bind(format!("{address}:{port}"))
        .map(|listener| listener.set_nonblocking(true))
    // here how to iterate over the stream like the official doc example? => for stream in listener.incoming() { ... }

Many thanks.

You can simply iterate inside the map closure:

    TcpListener
        ::bind(format!("{address}:{port}"))
        .and_then(|listener| listener.set_nonblocking(true))  // this returns only one layer of Result
        .map(|listener| {
            for stream in listener.incoming() { ... }
        })

Note that you still need to handle the Result, usually by changing your function to return it.

Another approach is to use the ? operator, which unwraps the result or returns early with the error:

fn handle_connections(address: &str, port: u16) -> std::io::Result<()> {
    let listener = TcpListener
        ::bind(format!("{address}:{port}"))?;
    listener.set_nonblocking(true)?;   // this also returns a result
    for stream in listener.incoming() { ... }
}

If you need your function to return a different error type you can use Result::map_err().

3 Likes

There's no one-size-fits-all solution. As the programmer, you need to decide what should happen when the program can't bind to the port that it requested. unwrap() does this by terminating the program immediately with a panic message that's useful for developers but pretty inscrutable to ordinary users. This is the simplest approach, which is why it's used in documentation, but usually isn't the best choice. If you're just writing something quick-and-dirty, though, it may be sufficient.

Other options include panicing with a nicer message (expect), returning a Result to the caller (who will then face the same problem), trying a different port/interface, giving up on networking altogether but otherwise continue running, etc.

5 Likes

Thanks. This is what I am looking for!

Thanks for the advice! I think I need to write more Rust code so that I can decide which solution is better fit my requirements.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.