I am honest with you. I am learning Rust but as IT consultant I am not able to run back to every new feature. For me, Rust is a revolutionary programming language. We all must admit it.
I cannot understand why the following code is working. I would like to understand the internals of why the call if let Ok(info) = count(file) {
is working if file
is a Box<dyn BufRead>
but count expects impl BufRead
. And how can i reach the same result of using a custom struct, say Person. Can someone explain that to me with an example and all the trait required to implement on a custom struct?
use anyhow::Result;
use std::fs::File;
use std::io::{self, BufRead, BufReader};
#[derive(Debug)]
/// Rust version of `wc`
struct Args {
/// Input file(s)
files: Vec<String>,
/// Show line count
lines: bool,
/// Show word count
words: bool,
/// Show byte count
bytes: bool,
/// Show character count
chars: bool,
}
#[derive(Debug, PartialEq)]
struct FileInfo {
num_lines: usize,
num_words: usize,
num_bytes: usize,
num_chars: usize,
}
// --------------------------------------------------
fn main() {
if let Err(e) = run(Args {
files: vec![String::from("file1.txt"), String::from("file2.txt")],
lines: true,
words: false,
bytes: true,
chars: false,
}) {
eprintln!("{e}");
std::process::exit(1);
}
}
// --------------------------------------------------
fn run(mut args: Args) -> Result<()> {
if [args.words, args.bytes, args.chars, args.lines]
.iter()
.all(|v| v == &false)
{
args.lines = true;
args.words = true;
args.bytes = true;
}
let mut total_lines = 0;
let mut total_words = 0;
let mut total_bytes = 0;
let mut total_chars = 0;
for filename in &args.files {
match open(filename) {
Err(err) => eprintln!("{filename}: {err}"),
Ok(file) => {
if let Ok(info) = count(file) {
println!(
"{}{}{}{}{}",
format_field(info.num_lines, args.lines),
format_field(info.num_words, args.words),
format_field(info.num_bytes, args.bytes),
format_field(info.num_chars, args.chars),
if filename == "-" {
"".to_string()
} else {
format!(" {filename}")
},
);
total_lines += info.num_lines;
total_words += info.num_words;
total_bytes += info.num_bytes;
total_chars += info.num_chars;
}
}
}
}
if args.files.len() > 1 {
println!(
"{}{}{}{} total",
format_field(total_lines, args.lines),
format_field(total_words, args.words),
format_field(total_bytes, args.bytes),
format_field(total_chars, args.chars)
);
}
Ok(())
}
// --------------------------------------------------
fn open(filename: &str) -> Result<Box<dyn BufRead>> {
match filename {
"-" => Ok(Box::new(BufReader::new(io::stdin()))),
_ => Ok(Box::new(BufReader::new(File::open(filename)?))),
}
}
// --------------------------------------------------
fn format_field(value: usize, show: bool) -> String {
if show {
format!("{value:>8}")
} else {
"".to_string()
}
}
// --------------------------------------------------
fn count(mut file: impl BufRead) -> Result<FileInfo> {
let mut num_lines = 0;
let mut num_words = 0;
let mut num_bytes = 0;
let mut num_chars = 0;
let mut line = String::new();
loop {
let line_bytes = file.read_line(&mut line)?;
if line_bytes == 0 {
break;
}
num_bytes += line_bytes;
num_lines += 1;
num_words += line.split_whitespace().count();
num_chars += line.chars().count();
line.clear();
}
Ok(FileInfo {
num_lines,
num_words,
num_bytes,
num_chars,
})
}