I am new to rust and wanted to get going with a medium-sized project to help learn. I've settled on creating some basic quantum mechanics code which starts with this bare-bones file reader. It takes a pdb file which is just a list of atom coordinates and the connections between them - with some extra information. I've included a basic pdb for testing below, the location string for the file requires changing.

I'm trying to use rust style as much as possible. Any suggestions on code/style improvement, no matter how small, are very welcome, thanks.


COMPND    methanol
HETATM    1   C  UNL     1      -0.372   0.002   0.003  1.00  0.00           C
HETATM    2   O  UNL     1       0.898  -0.575  -0.119  1.00  0.00           O
HETATM    3   H  UNL     1      -0.674  -0.119   1.051  1.00  0.00           H
HETATM    4   H  UNL     1      -0.314   1.066  -0.315  1.00  0.00           H
HETATM    5   H  UNL     1      -1.083  -0.525  -0.643  1.00  0.00           H
HETATM    6   H  UNL     1       1.545   0.150   0.024  1.00  0.00           H
CONECT    1    2    3    4    5
CONECT    2    6


use std::fs;

struct Atom {
    /// May include atomic charges, masses etc in future
    atomic_symbol: String,
    index: i16,
    coords: (f64, f64, f64),

struct Bond {
    atom_1_index: i16,
    atom_2_index: i16,

pub fn read_pdb() {
    /// Iterates over lines in any pdb file to produce vec of Atom structs
    /// Will be expanded in future to build graph of molecule as well

    let data = fs::read_to_string(
        .expect("Unable to read file");

    // vec of strings where each element in vec is a line in the file
    let lines: Vec<&str> = data.lines().collect();
    let name: &str = &lines[0][10..];
    let mut molecule = Vec::new();
    let mut bonds = Vec::new();

    // loop over each element in the vec, starting from the 1st (not 0th)
    for line in lines[1..].iter() {
        if &line[..3] == "HET" {
            // atomic_symbol may be 1/2 chars so it's defined over a range not a specific index
            let atom = Atom {
                atomic_symbol: line[13..15].trim().parse::<String>().unwrap(),
                // pdbs count from 1, hence the -1
                index: line[10..13].trim().parse::<i16>().unwrap() - 1,
                // trim removes whitespace when there isn't a minus sign
                coords: (
        } else if &line[..3] == "CON" {
            let parent_index = line[10..13].trim().parse::<usize>().unwrap() - 1;

            for atom_index in line[14..].split_whitespace() {
                let child_index = atom_index.parse::<usize>().unwrap() - 1;
                let bond = Bond {
                    atom_1_index: molecule[parent_index].index,
                    atom_2_index: molecule[child_index].index,
        } else {

    println!("{:?}\n{:?}\n{:?}\n", molecule, name, bonds);

Please be aware the very specific ranges for extracting the coords and such are from the official pdb format guide. Occasionally coords can overlap if using high precision e.g. -0.56789-0.12345 so generally slicing is used instead of splitting over whitespace.

EDIT: After trying to implement a graph structure using petgraph for the molecule, I instead just added a Vec of tuples for my bonds. This seems much easier than a (potentially) cyclic graph; I know they cause issues with the borrowing system. Anyway, the code is updated and now slightly longer than before but not really anymore complex.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.