官术网_书友最值得收藏!

  • Rust Programming By Example
  • Guillaume Gomez Antoni Boucher
  • 691字
  • 2021-07-02 19:13:07

Reading formatted data from files

As you will certainly have guessed at this point, we'll use iterators once again. This is what the loading function will look like:

fn line_to_slice(line: &str) -> Vec<u32> {
    line.split(" ").filter_map(|nb| nb.parse::<u32>().ok()).collect()
}

fn load_highscores_and_lines() -> Option<(Vec<u32>, Vec<u32>)> {
    if let Ok(content) = read_from_file("scores.txt") {
        let mut lines = content.splitn(2, "\n").map(|line| 
line_to_slice(line)).collect::<Vec<_>>(); if lines.len() == 2 { let (number_lines, highscores) = (lines.pop().unwrap(),
lines.pop().unwrap()); Some((highscores, number_lines)) } else { None } } else { None }
}

Once again, not easy to understand, at first sight. So let's explain all this!

fn line_to_slice(line: &str) -> Vec<u32> {

Our line_to_slice() function does the opposite of slice_to_string(); it transforms a &str into a slice of u32 (or &[u32]). Let's see the iterator now:

    line.split(" ").filter_map(|nb| nb.parse::<u32>().ok()).collect()

Just like last time, let's split the calls:

    line.split(" ")
      .filter_map(|nb| nb.parse::<u32>().ok())
      .collect()

A bit better! Now let's explain:

     line.split(" ")

We create an iterator that will contain all strings between spaces. So a b will contain a and b:

    .filter_map(|nb| nb.parse::<u32>().ok())

This method is particularly interesting since it's the merge of two others: filter() and map(). We already know map() but what about filter()? If the condition isn't verified (so if the returned value of the closure is false), the iterator won't pass the value to the next method call. filter_map() works the same at this point: if the closure returns None, the value won't be passed to the next method call.

Now let's focus on this part:

    nb.parse::<u32>().ok()

Here, we try to convert &str into u32. The parse() method returns a Result but the filter_map() expects an Option so we need to convert it. That's what the ok() method is for! If your Result is an Ok(value), then it'll convert it into a Some(value). However, if it's an Err(err), it'll convert it into a None (but you'll lose the error value).

To sum this up, this whole line tries to convert a &str into a number and ignores it if the conversion fails so it's not added to our final Vec. Amazing how much we can do with such small code!

And finally:

    .collect()

We collect all the successful conversions into a Vec and return it.

That's it for this function, now let's look at the other one:

    fn load_highscores_and_lines() -> Option<(Vec<u32>, Vec<u32>)> {

Here, if everything went fine (if the file exists and has two lines), we return an Option containing in the first position the highest scores and in the second position the highest number of lines:

    if let Ok(content) = read_from_file("scores.txt") {

So if the file exists and we can get its content, we parse the data:

    let mut lines = content.splitn(2, "\n").map(|line| 
line_to_slice(line)).collect::<Vec<_>>();

Another iterator! As usual, let's rewrite it a bit:

    let mut lines = content.splitn(2, "\n")
         .map(|line| line_to_slice(line))
         .collect::<Vec<_>>();

I think you're starting to get how they work, but just in case you don't know, here's how:

    content.splitn(2, "\n")

We make an iterator containing at most two entries (because of the 2 as the first argument) splitting lines:

    .map(|line| line_to_slice(line))

We transform each line into a Vec<u32> by using the function described in the preceding code:

    .collect::<Vec<_>>();

And finally, we collect those Vecs into a Vec<Vec<u32>>, which should only contain two entries.

Let's look at the next line now:

    if lines.len() == 2 {

As said before, if we don't have two entries inside our Vec, it means something is wrong with the file:

    let (number_lines, highscores) = (lines.pop().unwrap(), 
lines.pop().unwrap());

In case our Vec has two entries, we can get the corresponding values. Since the pop method removes the last entry of the Vec, we get them in reverse (even though we return high scores first then the highest number of lines):

    Some((highscores, number_lines))

Then everything else is just the error handling. As we said previously, if any error occurs, we return None. In this case, it's not really important to handle the error since it's just high scores. If we have errors with the sdl libraries, nothing will work as expected, so we need to handle them to avoid a panic.

It's now time to really start the game!

主站蜘蛛池模板: 安丘市| 永顺县| 黔江区| 乐陵市| 常宁市| 肥城市| 永济市| 双桥区| 葫芦岛市| 右玉县| 镇巴县| 蓝山县| 开原市| 孟村| 寿阳县| 通化县| 清河县| 辽宁省| 漯河市| 寿光市| 郓城县| 凤阳县| 尉犁县| 本溪市| 商洛市| 三穗县| 琼结县| 英德市| 三门县| 石河子市| 陇西县| 嘉义市| 柯坪县| 潮安县| 沈丘县| 泰来县| 千阳县| 都安| 临邑县| 白银市| 鹤岗市|