Reading and storing a list of periodic elements from a file


#1

I am completely new to Rust and was wandering how I would go about retrieving a list of all elements of the periodic table with their name, symbol, relative atomic mass and atomic number. The file looks something like this:

Element:       Symbol     Atomic number     Atomic mass
Aluminum	Al	        13	                   26.981539

So a struct would look like this:

struct Element {
	name: str,
	symbol: str,
	an: f64,
	am: f64,
}	

I would like to store them somehow in the program with their name, symbol, atomic number and atomic mass or write a function that cycles through the file and can print a given element’s atomic number, or mass etc to console.

Thanks in advance, :slightly_smiling:


#2

String is the string type you will will want to use for name and symbol (unless you want an enum instead, to put this information completely in types). Also, atomic number is an integer, so an unsigned integer like u32 will work well.


#3

If you really are “completely new”, then my advice would be to read the book. Explaining how to do this without you having read the book means explaining the difference between owned and borrowed types, error handling, loops, etc., etc., most of which should already be covered by the book.

There’s also Rust by Example.

Assuming you’ve read and largely understood those, here’s how you might do it:

// Pull in stuff from the standard library.
use std::fs::File;
use std::io::{self, Read};

#[derive(Debug)]
struct Element {
    name: String,
    symbol: String,
    an: u8,
    aw: f64,
}

fn main() {
    let mut elements: Vec<Element> = vec![];

    // Read the contents of `table.txt` into memory.
    let mut table = String::new();
    let mut table_file = File::open("table.txt")
        .expect("opening `table.txt` failed.");
    table_file.read_to_string(&mut table)
        .expect("reading `table.txt` failed.");

    // Turn table into a sequence of lines, skip the first, and then...
    for line in table.lines().skip(1) {
        // Split the line into words.
        let parts = line.split_whitespace();

        // Try to get the first four words.
        match (parts.next(), parts.next(), parts.next(), parts.next()) {
            (Some(name), Some(symbol), Some(an), Some(aw)) => {
                // Create an `Element` and append it to our list.
                elements.push(Element {
                    name: String::from(name),
                    symbol: String::from(symbol),
                    an: an.parse(),
                    aw: aw.parse(),
                });
            },
            _ => continue
        }
    }

    // Use `&elements` because we just want to temporarily borrow the `elements`
    // vector.  Without `&`, we'd take ownership of it and *consume* it; we
    // couldn't use it again afterwards.
    for element in &elements {
        println!("{:?}", element);
    }
}

Also, although it’s still under development, this is pretty much exactly what scan-rules was written for, so here’s an example using scan-rules.