Compare efficient line-by-line file reading in Go and Rust
Memory-efficient streaming I/O idioms.
Go uses bufio.Scanner with ScanLines/Scan(); Rust uses BufReader with lines() or read_line().
Loading the whole file with ioutil.ReadFile or fs::read_to_string.
WHAT THIS TESTS: This question tests whether you understand buffered streaming I/O and can avoid loading multi-gigabyte files into RAM. It also checks API fluency in both languages, specifically the bufio package in Go and the std::io module in Rust. Interviewers want to see that you know the concrete types, the loop patterns, and the error-handling gotchas that differ between the two ecosystems.
A GOOD ANSWER COVERS: First, in Go, you open the file with os.Open, then wrap the io.Reader in a bufio.Scanner via bufio.NewScanner. You set the split function with scanner.Split(bufio.ScanLines), then loop with scanner.Scan() and read each line with scanner.Text() or scanner.Bytes(). Mention that Scanner has a default 64K line limit and you can raise it with scanner.Buffer. Second, in Rust, you open the file with std::fs::File::open, then wrap it in std::io::BufReader::new(file). You can either iterate with buf_reader.lines() or call buf_reader.read_line(&mut buffer) in a loop, reusing the String buffer to avoid allocations. Third, contrast the two: Go's Scanner returns tokens and hides the underlying buffer, while Rust's BufReader is an adapter that implements BufRead and exposes lines() as an iterator or read_line for manual control. Fourth, mention error handling: in Go you check scanner.Err() after the loop; in Rust you handle Result inside the iterator or from the read_line call.
COMMON WRONG ANSWERS: A major red flag is suggesting ioutil.ReadFile or os.ReadFile in Go, or fs::read_to_string in Rust, because those load the entire file into memory. Another red flag is using bufio.Reader.ReadString('\n') in Go without explaining why you might prefer Scanner for simple line splitting, or forgetting to check scanner.Err() after looping. In Rust, a common mistake is calling File::open and reading directly without BufReader, which causes a syscall per byte and destroys performance. Also, collecting the Lines iterator into a Vec<String> defeats the purpose of streaming.
LIKELY FOLLOW-UPS: The interviewer might ask how to handle lines larger than Scanner's buffer in Go, which leads to scanner.Buffer. They might ask how to preserve the newline character, which means using ScanBytes or ReadString instead of ScanLines. In Rust, they might ask about zero-copy approaches, which leads to std::io::BufRead::read_until or memmap2 for true zero-copy. They could also ask about parallel processing, which opens the door to channels in Go or rayon in Rust, but only after establishing the sequential streaming baseline.
ONE CONCRETE EXAMPLE: Imagine processing a ten-gigabyte log file on a machine with two gigabytes of RAM. In Go, you write scanner := bufio.NewScanner(file), scanner.Split(bufio.ScanLines), then for scanner.Scan() { process(scanner.Text()) }, followed by if err := scanner.Err(); err != nil { return err }. In Rust, you write let reader = std::io::BufReader::new(file); let mut line = String::new(); while reader.read_line(&mut line)? > 0 { process(&line); line.clear(); }. Both approaches keep memory usage flat regardless of file size.
Read the original → pkg.go.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.