LoginSignup
5
5

More than 5 years have passed since last update.

[Rust] RustでFizzBuzz書いてみる

Last updated at Posted at 2018-03-26

これを見て、なるほど、と思ったので書いてみることにした。

パターン1 まずは自分なりに書いてみる

FizzとBuzzとFizzBuzzをそれぞれ以下のように考える

  • Fizz
    • 3で割り切れるならFizzっていう人
  • Buzz
    • 5で割り切れるならBuzzっていう人
  • FizzBuzz
    • FizzとBuzzの組み合わせ
pub struct Fizz {
    values: [Option<&'static str>; 3],
}

impl Fizz {
    pub fn new() -> Fizz {
        Fizz { values: [Some("Fizz"), None, None] }
    }

    fn get(&self, i: usize) -> Option<&'static str> {
        self.values[i%3]
    }
}

pub struct Buzz {
    values: [Option<&'static str>; 5],
}

impl Buzz {
    pub fn new() -> Buzz {
        Buzz { values: [Some("Buzz"), None, None, None, None] }
    }
    fn get(&self, i: usize) -> Option<&'static str> {
        self.values[i%5]
    }
}

fn main() {
    let fizz = Fizz::new();
    let buzz = Buzz::new();

    for i in 1..50 {
        match (fizz.get(i), buzz.get(i)) {
            (None, None) => println!("{}", i),
            (fizz, buzz) => println!("{}{}", fizz.unwrap_or_default(), buzz.unwrap_or_default()),
        }
    }
}

割り切れるかどうか、ではなくて、剰余をindexとして使いたかったので、配列として持っている。

本当は定数として、構造体のfieldに持って Buzz::get(i) のような感じにしたかったけど、
構造体の中で staticconst はできないらしい。
[修正] 1.20(stable)以降ではAssociated constで実現できる

やるとしたら、こんな感じだろうか。

const FIZZ_VALUES: [Option<&'static str>; 3] = [Some("Fizz"), None, None];
const BUZZ_VALUES: [Option<&'static str>; 5] = [Some("Buzz"), None, None, None, None];

pub struct Fizz;
impl Fizz {
    fn get(i: usize) -> Option<&'static str> {
        FIZZ_VALUES[i%3]
    }
}

pub struct Buzz;
impl Buzz {
    fn get(i: usize) -> Option<&'static str> {
        BUZZ_VALUES[i%5]
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn fizzbuzz() {
        for i in 1..50 {
            match (Fizz::get(i), Buzz::get(i)) {
                (None, None) => println!("{}", i),
                (fizz, buzz) => println!("{}{}", fizz.unwrap_or_default(), buzz.unwrap_or_default()),
            }
        }
    }
}

パターン2

冒頭で触れた記事のトレイトの使い方になるほど感があったので、マネしてみる。

  • FizzとBuzzは、同じ種類と考える
    • つまりEnumだ
  • Fizz(Buzz)がどう発言するか
    • つまり、Displayトレイトだ
  • iがどういう時に、Fizzになるか、Buzzになるか
    • つまり、Fromトレイトだ
    • x.into() って書けるとカッコイイ
use std::fmt;

enum FizzBuzz {
    Fizz,
    Buzz,
    FizzBuzz,
    Num(u32),
}

impl fmt::Display for FizzBuzz {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &FizzBuzz::Fizz     => write!(f, "Fizz"),
            &FizzBuzz::Buzz     => write!(f, "Buzz"),
            &FizzBuzz::FizzBuzz => write!(f, "FizzBuzz"),
            &FizzBuzz::Num(i)   => write!(f, "{}", i),
        }
    }
}

impl From<u32> for FizzBuzz {
    fn from(n: u32) -> FizzBuzz {
        match (n%3, n%5) {
            (0, 0) => FizzBuzz::FizzBuzz,
            (0, _) => FizzBuzz::Fizz,
            (_, 0) => FizzBuzz::Buzz,
            _      => FizzBuzz::Num(n),
        }
    }
}

fn main() {
    (1..50)
        .map(|x| x.into())
        .for_each(|r: FizzBuzz| println!("{}", r) )
}

すこしシンプルになったかな?

Into<U> for TFrom<T> for U

↑では From<T> を実装したことで into() が使えるようになった。
でも、ここで疑問

Into を実装するのと、何が違うんだろうか

Fromには

From for U implies Intofor T
from is reflexive, which means that From for T is implemented

と書かれている。

// From implies Into
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, U> Into<U> for T where U: From<T>
{
    fn into(self) -> U {
        U::from(self)
    }
}

ここかな?

From<T> for U
というのは
TからUへ変換することができる
ということで

それはつまり
TはUになることが出来る
ということで、暗黙的に
Into<U> for T
となることができるはず

っていうことですかね?

つまり、

impl Into<Hoge> for Foo {
  
}

を手で書くことはほとんどなくて、それがやりたい時はFromを実装する。

Intoを使う場面は、型指定で、特定の何かにintoできるもの というのを表現したい時に使う。

と理解しておけばいいのだろうか?

今回のケースでいうと

fn print_fizzbuzz<T: Into<FizzBuzz>>(x: T) {
    println!("{}", x.into())
}

fn main() {
    (1..50).for_each(|x| print_fizzbuzz(x) )
}

こんな感じでしょうか。

5
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5