Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
5
Help us understand the problem. What is going on with this article?
@yagince

[Rust] RustでFizzBuzz書いてみる

More than 3 years have passed since last update.

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

パターン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
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
yagince

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
5
Help us understand the problem. What is going on with this article?