Skip to content

Instantly share code, notes, and snippets.

@szabgab
Last active September 28, 2025 22:57
Show Gist options
  • Select an option

  • Save szabgab/cccc47e11492f4576aa0567731c224b6 to your computer and use it in GitHub Desktop.

Select an option

Save szabgab/cccc47e11492f4576aa0567731c224b6 to your computer and use it in GitHub Desktop.
tail -f in rust
use std::{env, path::PathBuf, time::Duration};
use tokio::fs::File;
use tokio::io::{AsyncBufReadExt, AsyncSeekExt, BufReader, SeekFrom};
use tokio::time::sleep;
#[tokio::main]
async fn main() {
let filename = get_filename();
tail_file(filename).await;
}
async fn tail_file(filename: PathBuf) {
let mut file = File::open(&filename).await.expect("Failed to open file");
file.seek(SeekFrom::End(0)).await.expect("Failed to seek");
loop {
sleep(Duration::from_millis(500)).await;
let mut reader = BufReader::new(&mut file);
let mut buf = String::new();
while reader
.read_line(&mut buf)
.await
.expect("Failed to read line")
> 0
{
print!("{}", buf);
buf.clear();
}
}
}
fn get_filename() -> PathBuf {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: {} <filename>", args[0]);
std::process::exit(1);
}
PathBuf::from(&args[1])
}
@chenya
Copy link

chenya commented Sep 16, 2025

Hi.
Nice little tail implementation!

A quick suggestion:
Your code uses std::fs::File and blocking reads on the tokio runtime, which can block the runtime and hurt concurrency.
You can use the Tokio's async file APIs (tokio::fs::File + tokio::io::AsyncBufReadExt / AsyncSeekExt) so the program doesn't block other async tasks while waiting for I/O.

Here is a small async rewrite you can use as a drop-in suggestion:

use std::{env, path::PathBuf};
use tokio::{
    fs::File,
    io::{AsyncBufReadExt, AsyncSeekExt, BufReader as AsyncBufReader, SeekFrom},
    time::{Duration, sleep},
};

#[tokio::main]
async fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() != 2 {
        eprintln!("Usage: {} <filename>", args[0]);
        std::process::exit(1);
    }
    let filename = PathBuf::from(&args[1]);

    // Open file and seek to end
    let mut file = File::open(&filename).await.expect("Failed to open file");
    let _pos = file.seek(SeekFrom::End(0)).await.expect("Failed to seek");

    loop {
        sleep(Duration::from_millis(500)).await;
        let mut reader = AsyncBufReader::new(&mut file);
        let mut buf = String::new();
        while reader.read_line(&mut buf).await.expect("Failed to read line") > 0 {
            print!("{}", buf);
            buf.clear();
        }
        let _pos = reader.stream_position().await.expect("Failed to get position");
    }
}

@szabgab
Copy link
Author

szabgab commented Sep 16, 2025

@chenya thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment