Is there a better way to read in a labeled data file?

I need my programs to be able to read in a labeled input file, perform calculations, and save it to an output file. I wrote a basic program that calculates the hypotenuse of a right triangle by taking in the other sides with the following input file.

input.txt:

a, 3.0
b, 4.0

This is my current solution which outputs a number to a text file in the Outputs folder:

use std::fs::File;
use std::fs;
use std::io::{self, BufRead};
use std::path::Path;

fn main() {
    // Creating an array to save the file inputs
    let mut data_array: [f32; 2] = [0.0;2];

    // reading the file inputs
    if let Ok(lines) = read_lines("Inputs/input.txt") {
        let mut i = 0;
        for line in lines {
            if let Ok(ip) = line {
                let split = ip.split(", ");
                let vec: Vec<&str> = split.collect();
                data_array[i] = vec[1].trim().parse().expect("Check Number");
                i = i + 1;
            }
        }
    }
    
    // Saving the numbers to variables
    let a: f32 = data_array[0];
    let b: f32 = data_array[1];
    
    // Writing out hypotenuse to file
    fs::write("Outputs/output.txt", ((a*a) + (b*b)).sqrt().to_string())
        .expect("Unable to write to file");
}

// The recommended way to read in by lines from the Rust Cookbook
// https://doc.rust-lang.org/rust-by-example/std_misc/file/read_lines.html
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

My current solution feels slightly obtuse but does get the job done. Is there a better way to accomplish this task?

Here's a vastly simplified version.

The most important modifications were:

  • Don't use array indexing for iteration. Use iterators.
  • Don't use array indexing for destructuring entire fixed-size arrays. Use pattern matching.
  • Handle errors instead of panicking on them or worse yet, ignoring them. Instead of deeply nested trees of if-let, use the ? operator.
  • Don't convert the result to string, that's an unnecessary allocation. If you need to write out formatted data, use the write!() family of macros, which take a format string and write directly to the file without any intermediate strings.
  • Don't compute the hypotenuse by hand, it's not as trivial as you might think. f32::hypot() is a numerically stable implementation that ensures good-quality results even in edge cases related to very small and very large floating-point numbers.
3 Likes

Thank you very much @H2CO3 ! Your explanation and code sample are extremely helpful! I'm still getting used to how Rust is structured as opposed to Fortran90.