1
\$\begingroup\$

Abstract

I am a beginner of Rust language. As my practice I want to create simple train reservation system.

I know little about the "best practice" in Rust, so I want some advises in my code. In terms of the function there are much luck to improve, but I want advises about how to write codes "in Rust".

Code structure

reservation
├── Cargo.lock
├── Cargo.toml
├── src
│   ├── lib.rs
│   ├── main.rs
│   └── reserve_request
│       └── mod.rs
└── target

My codes

lib.rs

pub mod reserve_request;

reserve_request/mod.rs

use std::collections::HashMap;

#[derive(PartialEq, Eq, Hash)]
pub struct Stations<'a> {
    pub start: &'a str,
    pub destination: &'a str,
}

pub struct Request<'a> {
    pub start: &'a str,
    pub destination: &'a str,
    pub time: &'a str,
    pub time_kind: &'a str,
}

impl<'a> Request<'a> {
    pub fn reserve(&self, timetable: HashMap<Stations, &[&str]>) -> bool {
        let st = Stations {
            start: self.start,
            destination: self.destination,
        };

        let times = timetable.get(&st);
        let default: &[&str] = &[];
        let result = match times {
            Some(r) => r,
            None => default,
        };

        for t in result {
            if *t == self.time {
                return true;
            }
        }

        false
    }

    pub fn is_valid(&self) -> bool {
        if self.start == "" {
            println!("You need to determine start point");
            return false;
        } else if self.destination == "" {
            println!("You need to determine destination");
            return false;
        } else if self.start == self.destination {
            println!("Invalid. The start point and destication is same");
        } else if self.time == "" && self.time_kind != "" {
            println!("Invalid time specification");
            return false;
        }

        true
    }
}

main.rs

use reserve::reserve_request;
use std::collections::HashMap;

fn main() {
    //TODO: save time table in the json file
    let time: &[&str] = &["12:00", "14:00", "18:00", "19:00"];
    let mut stations = HashMap::new();
    stations.insert(
        reserve_request::Stations {
            start: "Tokyo",
            destination: "Kyoto",
        },
        time,
    );

    let request = reserve_request::Request {
        start: "Tokyo",
        destination: "Kyoto",
        time: "18:00",
        time_kind: "start",
    };

    if request.is_valid() && request.reserve(stations) {
        println!("Succeed in reserving that train");
    } else {
        println!("Failed to reserved that train");
    }
}

What I want to know especially

  • In main.rs, to make a value of the HashMap (type &[&str]), I define time variable and insert into HashMap. I feel it is redundant but I do not know how to deal with.

  • Similar to above, in mod.rs, I make default variable for using in match clause only.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$
let time: &[&str] = &["12:00", "14:00", "18:00", "19:00"];
let mut stations = HashMap::new();
stations.insert(
    reserve_request::Stations {
        start: "Tokyo",
        destination: "Kyoto",
    },
    time,
);

Ok, I assume you tried putting the value assigned to time directly in the expression, but were unable to resolve the resulting error message. The problem is that expression has the type [&str; 4], an array. But you want a slice: &[&str] instead. There a few ways to resolve this:

Firstly, you can specify the type of the HashMap:

let mut stations: HashMap<reserve_request::Stations, &[&str]> = HashMap::new();
stations.insert(
    reserve_request::Stations {
        start: "Tokyo",
        destination: "Kyoto",
    },
    &["12:00", "14:00", "18:00", "19:00"]
);

This way, Rust will infer what type you actually wanted at convert the reference to an array into a slice.

Alternatively, you can manually request a slice by called .as_ref() on the array:

let mut stations = HashMap::new();
stations.insert(
    reserve_request::Stations {
        start: "Tokyo",
        destination: "Kyoto",
    },
    ["12:00", "14:00", "18:00", "19:00"].as_ref()
);

However, a better approach would be to not use arrays at all. Rust arrays are really just not that useful, and you almost always should prefer to use a Vec. Furthermore, if you implement loading from JSON as your comment suggests, then you'll certainly get a Vec and not a slice.

let mut stations = HashMap::new();
stations.insert(
    reserve_request::Stations {
        start: "Tokyo",
        destination: "Kyoto",
    },
    vec!["12:00", "14:00", "18:00", "19:00"]
);

Moving on the second part where you had an issue:

    let times = timetable.get(&st);
    let default: &[&str] = &[];

Firstly, we have the same problem as before with a (in this case empty) array. It can be solved in the same way as above.

    let result = match times {
        Some(r) => r,
        None => default,
    };

Option has a method, unwrap_or which does the same thing:

    let result = timetable.get(&st).unwrap_or([].as_slice());

However, a better approach would be use an if let statement.

    if let Some(times) = timetable.get(&st) {
        for t in times {
            if *t == self.time {
                return true;
            }
        }
    }

This way the loop over times only runs if it was present and you don't need to create and iterate over an empty slice.

However, if you use Vec instead of slices as I suggest, the inner loop can be written as a call to the contains function:

        if times.contains(&self.time) {
            return true;
        }

Or you can replace the whole function with:

        timetable
            .get(&Stations {
                start: self.start,
                destination: self.destination
            })
            .map_or(false, |times| times.contains(&self.time))

map_or returns false if the time table was not found, otherwise it calls the closure which checks if times contains the appropriate time.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.