『Advent of Rust 🦀 2024』 やってみた

  • #Rust

https://www.rustfinity.com/advent-of-rust

↑をやってみる

❗ネタバレ注意

Day1

attach_message_to_present() へ所有権を移しているので、 main() の println!() マクロでメッセージを出力できない→ clone() して、新たにヒープにメモリ確保しようというものだった

しかし、これはベストプラクティスではないと言う話、、、

pub fn main() {
    let gift_message = String::from("Merry Christmas! Enjoy your gift!");
    attach_message_to_present(gift_message.clone());

    println!("{}", gift_message);
}

// Don't change this function!
pub fn attach_message_to_present(message: String) {
    println!("The present now has this message: {}", message);
}

Day2 - Memory doesn’t grow on Christmas trees 🎄

Day1 の、新しく clone() する方法はベストプラクティスではないという話から、ではどうすればいいのかという話へ

clone() では新しくメモリに確保していたが、そうではなく参照を渡してあげれば解決する問題であった → &str を渡すことに

pub fn main() {
    let gift_message = String::from("Merry Christmas! Enjoy your gift!");
    attach_message_to_present(&gift_message);

    println!("{}", gift_message);
}

pub fn attach_message_to_present(message: &str) {
    println!("The present now has this message: {}", message);
}

Day3: Restoring the Nice List

(タイトルに統一感ないの気になるな)

Day3 は、型キャストの話だった

f32 (符号あり小数点あり) と u32 (符号なし)を計算するために as を使用した

// We need to find the nice and naughty kids for santa

// Each good deed is worth 1 point and each bad deed is worth 2 points
pub const GOOD_WEIGHT: f32 = 1.0;
pub const BAD_WEIGHT: f32 = 2.0;

pub fn is_nice(good_deeds: u32, bad_deeds: u32) -> bool {
    if good_deeds == 0 && bad_deeds == 0 {
        return false;
    }
    let good_score = good_deeds as f32 * GOOD_WEIGHT;
    let bad_score = bad_deeds as f32 * BAD_WEIGHT;
    let total_score = good_score / (good_score + bad_score);

    return total_score >= 0.75;
}

Day 4: Structifying the Naughty List

だんだんボリューミーになってきた

やること

  1. Add two variants to the Niceness enum: Nice and Naughty. Nice takes the number of good deeds.
  2. Add two fields to the Kid struct: name of type String and niceness of type Niceness.
  3. Move the is_nice function we created on Day 3 to an associated function of the Kid struct.
  4. Finally, implement the new() associated function for the Kid struct.

Niceness を enum で定義し、Kid のコンストラクタで is_nice() を判定し、 enum を埋めて返すというもの

追加で、 is_nice() 関数を、 Kid の関連関数としてまとめている

pub const GOOD_WEIGHT: f32 = 1.0;
pub const BAD_WEIGHT: f32 = 2.0;

#[derive(Debug, PartialEq)] // needed for tests
pub enum Niceness {
    Nice(u32),
    Naughty,
}

pub struct Kid {
    pub name: String,
    pub niceness: Niceness,
}

impl Kid {
    fn is_nice(good_deeds: u32, bad_deeds: u32) -> bool {
        if good_deeds == 0 && bad_deeds == 0 {
            return false;
        }

        let good_deeds = good_deeds as f32 * GOOD_WEIGHT;
        let bad_deeds = bad_deeds as f32 * BAD_WEIGHT;

        let ratio = good_deeds / (good_deeds + bad_deeds);

        ratio >= 0.75
    }

    pub fn new(name: String, good_deeds: u32, bad_deeds: u32) -> Kid {
        if Self::is_nice(good_deeds, bad_deeds) {
            return Self{
                name,
                niceness: Niceness::Nice(good_deeds),
            };
        } else {
            return Self{
                name,
                niceness: Niceness::Naughty,
            };
        }
    }
}

Day 5: Parsing naughty-list.csv

初めて使ったメソッドがいろいろあったのでまとめる

.next()

イテレータを進め、次の値を返す。返す値は Some()

ok_or()

Transforms the Option into a Result<T, E>, mapping Some(v) to Ok(v) and None to Err(err).

.parse::<T>()

Parses this string slice into another type.

型を指定してパースする

let four: u32 = "4".parse().unwrap(); // これだと危なくない?

assert_eq!(4, four);

let four = "4".parse::<u32>(); // こうすると Some にしてくれる?

assert_eq!(Ok(4), four);

xxx()?: ? 演算子

https://zenn.dev/hakoten/articles/8ae9dd0d3a2080#%3F-%E6%BC%94%E7%AE%97%E5%AD%90

?演算子は、Resultを簡単に扱うための構文糖衣で、関数がResult型を返す場合、?を使うことでエラーが発生したら早期にその関数からリターンすることができる。

.map_err()

これがわかりやすかった

? 演算子は同じErrのみ返せるが、エラーの型が違うと同じ返り値として使えないので、 err を変換するというもの

今回は返り値が &'static str だが、 parse::<u32>() の Err は ParseIntErr だったので、文字列のエラーに変換した

Answer

pub const GOOD_WEIGHT: f32 = 1.0;
pub const BAD_WEIGHT: f32 = 2.0;

#[derive(Debug, PartialEq)]
pub enum Niceness {
    Nice(u32),
    Naughty,
}

pub struct Kid {
    pub name: String,
    pub niceness: Niceness,
}

const NONE_FIELD_ERR: &str = "None Feild.";
const PARSE_ERR: &str = "Could not Parse";

impl Kid {
    pub fn parse_row(csv_row: &str) -> Result<Kid, &'static str> {
        let mut fields = csv_row.split(',');
        let name = fields.next().ok_or(NONE_FIELD_ERR)?.to_string();
        let good_deeds = fields.next().ok_or(NONE_FIELD_ERR)?.parse::<u32>().map_err(|_| PARSE_ERR)?;
        let bad_deeds = fields.next().ok_or(NONE_FIELD_ERR)?.parse::<u32>().map_err(|_| PARSE_ERR)?;

        Ok(Self::new(name, good_deeds, bad_deeds))
    }

    pub fn new(name: String, good_deeds: u32, bad_deeds: u32) -> Self {
        let niceness = if Self::is_nice(good_deeds, bad_deeds) {
            Niceness::Nice(good_deeds)
        } else {
            Niceness::Naughty
        };

        Self { name, niceness }
    }

    pub fn is_nice(good_deeds: u32, bad_deeds: u32) -> bool {
        if good_deeds == 0 && bad_deeds == 0 {
            return false;
        }

        let good_deeds = good_deeds as f32 * GOOD_WEIGHT;
        let bad_deeds = bad_deeds as f32 * BAD_WEIGHT;

        let ratio = good_deeds / (good_deeds + bad_deeds);

        ratio >= 0.75
    }
}
← 記事一覧へ