let listener = match TcpListener::bind("127.0.0.1:7878") {
Ok(v) => {
println!("Sever started on {:?}", v.local_addr()); // this prints: "Server started on Ok(127.0.0.1:7878)"
v
}
Err(e) => {
println!("Error: {}", e.to_string());
return;
}
};
How can I make that Ok() share the value from within? I tried: v.local_addr().into_ok(), but it yells at me:
the trait bound `!: From<std::io::Error>` is not satisfied
required for `std::io::Error` to implement `Into<!>`
What do you want to do when local_addr() is an Err? Rust is forcing you to think about that possibility.
The simplest thing to do is use the ? operator to return errors to the caller so you only have to deal with the green path. To do this make sure your function returns a Result<_, std::io::Error>, then you can write:
let listener = TcpListener::bind("127.0.0.1:78878")?;
println!("Server started on {:?}", v.local_addr()?);
Result::into_ok is for use with an uninhabited error. Getting the TcpListener::local_addr can fail with an io::Error, so you need to handle that in some way.
Either you can match the result from .local_addr() to handle Ok and Err separately, or if you're confident that the local addr will always be available, you can just .unwrap() the result.
I'm sure there is a very good reason why the language was designed this way, but in my mind, I was hoping for a simple (intrinsic) way of having the Resultenum itself... both, stringify and expose (return) the value from Ok() that can be easily printed out, in one single method (ie: .get_ok().to_string()).
Another thing I seem to be struggling with is this:
let listener = match TcpListener::bind("127.0.0.1:7878") {
Ok(v) => {
// once the "control flow" made it past this point, it is pretty much guaranteed that we are only dealing
// with the Ok() part, so "v.local_addr()" should probably shave off the Ok() from the println!(), right?
// so I should be able to access v.local_addr().to_string() from the Result enum, and perhaps only
// use the "{}" instead of the "{:?}" - but clearly, I'm missing something!
println!("Sever started on {:?}", v.local_addr()); // this prints: "Server started on Ok(127.0.0.1:7878)"
v
}
Err(e) => {
println!("Error: {}", e.to_string());
return;
}
};
Using a match statement to access the value inside an enum is the simple intrinsic way to get at the value.
You can also use unwrap() if you want to handle the error by just crashing.
It sounds like you are assuming that just because we've bound to a port and have a TCP listener, we will always be able to access the local IP address.
However, this isn't always the case. To find the local address, we need to ask the OS what IP address and port is associated with a listener. This operation may fail, so local_addr() needs to return a Result<SocketAddr> so you can handle that error. The Result doesn't come from TcpListener::bind(), but from querying the OS.
ERRORS
EBADF The argument sockfd is not a valid file descriptor.
EFAULT The addr argument points to memory not in a valid part of
the process address space.
EINVAL addrlen is invalid (e.g., is negative).
ENOBUFS
Insufficient resources were available in the system to
perform the operation.
ENOTSOCK
The file desriptor sockfd does not refer to a socket.
Thank you for clarifying. Just out of curiosity: Would it be possible/viable to pass/redirect an error down into the same Ok() <--> Err(e) checking with the ? operator, such that the user would be able to catch the very first error in the error-stack-trace hierarchy, without the need for nesting multiple Results?
If it were me, I'd just write something like this:
fn listen_and_serve(bind_addr: &str) -> Result<()> {
let listener = TcpListener::bind(bind_addr)?;
let local_addr = listener.local_addr()?;
println!("Listening on {local_addr}");
for stream in listener.incoming() {
// do something with the TCP connection
}
Ok(())
}
The key part is returning a Result so you can use ? to bail out if reading the local address fails. Looking at the types of errors you get on Linux (from that man page), I'd expect local_addr() errors to be very rare and indicate something is seriously wrong with the world, so returning from listen_and_serve() with an error is probably the right approach.