How do I close a Unix socket in Rust? How do I close a Unix socket in Rust? unix unix

How do I close a Unix socket in Rust?


Please learn to create a minimal reproducible example and then take the time to do so. In this case, there's no need for threads or functions or testing frameworks; running this entire program twice reproduces the error:

use std::os::unix::net::UnixListener;fn main() {    UnixListener::bind("/tmp/my_socket.sock").unwrap();}

If you look at the filesystem before and after the test, you will see that the file /tmp/my_socket.sock is not present before the first run and it is present before the second run. Deleting the file allows the program to run to completion again (at which point it recreates the file).

This issue is not unique to Rust:

Note that, once created, this socket file will continue to exist, even after the server exits. If the server subsequently restarts, the file prevents re-binding:

[...]

So, servers should unlink the socket pathname prior to binding it.

You could choose to add some wrapper around the socket that would automatically delete it when it is dropped or create a temporary directory that is cleaned when it is dropped, but I'm not sure how well that would work. You could also create a wrapper function that deletes the file before it opens the socket.

Unlinking the socket when it's dropped

use std::path::{Path, PathBuf};struct DeleteOnDrop {    path: PathBuf,    listener: UnixListener,}impl DeleteOnDrop {    fn bind(path: impl AsRef<Path>) -> std::io::Result<Self> {        let path = path.as_ref().to_owned();        UnixListener::bind(&path).map(|listener| DeleteOnDrop { path, listener })    }}impl Drop for DeleteOnDrop {    fn drop(&mut self) {        // There's no way to return a useful error here        let _ = std::fs::remove_file(&self.path).unwrap();    }}

You may also want to consider implementing Deref / DerefMut to make this into a smart pointer for sockets:

impl std::ops::Deref for DeleteOnDrop {    type Target = UnixListener;    fn deref(&self) -> &Self::Target {        &self.listener    }}impl std::ops::DerefMut for DeleteOnDrop {    fn deref_mut(&mut self) -> &mut Self::Target {        &mut self.listener    }}

Unlinking the socket before it's opened

This is much simpler:

use std::path::Path;fn bind(path: impl AsRef<Path>) -> std::io::Result<UnixListener> {    let path = path.as_ref();    std::fs::remove_file(path)?;    UnixListener::bind(path)}

Note that you can combine the two solutions, such that the socket is deleted before creation and when it's dropped.

I think that deleting during creation is a less-optimal solution: if you ever start a second server, you'll prevent the first server from receiving any more connections. It's probably better to error and tell the user instead.