I’ve been playing with some file-related functionality in Rust, and I noticed that my way of reading files and preparing output feels really long, even for simple tasks. I use std::fs::read_to_string, or sometimes BufReader if I need line-by-line reading. I wrap almost everything in functions that return Result<_, std::io::Error> and match errors inside the function instead of just using ?. I do this to print custom error messages depending on what went wrong — like file not found, or permission denied. It works fine, but when I compare it to other examples people post, they often just use ? in main() and let it crash or bubble the error with minimal handling. Is it really better in practice to avoid all this extra error matching?
When I'm reading multiple files in a loop, I also check if the file path is valid, readable, and sometimes try to clean the input manually before opening it. I made helper functions like read_file, validate_file, and clean_path just to break things up. I like the control, but I’m worried that I’m making it too complex for what Rust expects. Does anyone here avoid this kind of splitting and just keep everything inline in a for loop with some ?? I'd really like to understand how other users approach this.
One part that actually made me think more about how to simplify was when I added ZIP creation using the zip crate. I got the idea from using a free file zipping website that lets you drop multiple files and get one ZIP file in return. It felt very natural to do something like that locally in Rust. I read each file into memory, then added it to the ZIP with ZipWriter::start_file and write_all. That process worked better than expected, and I was happy with it. It also made me wonder if I'm overthinking other parts of the code.
My question is mainly about structure. Is there a preferred way in Rust to deal with things like file I/O, zipping, and error messages without making everything look like a full-blown error-handling system? Do people usually go minimal and let the system print generic errors, or is it common to match on every possible ErrorKind manually? And when it comes to organizing file logic, do you separate your reading, checking, and writing into separate functions even for short tools, or just do it inline? I’d like to see how others approach this when you're reading several files, transforming or bundling them, and writing some kind of result.
Also, is it considered weird in the Rust community to avoid things like anyhow or thiserror when I’m just writing simple tools and want to stick with std::io::Error and basic matching? I keep it plain on purpose, but I wonder if I’m just doing more work for no real benefit. Would really appreciate if others could share how they handle this kind of situation.