
Why your first Rust FizzBuzz implementation may not work - chrismorgan
http://chrismorgan.info/blog/rust-fizzbuzz.html
======
icedog
Putting the String issue aside, I just wanted to show the beauty of pattern
matching.

    
    
        for i in range(1i, 101) {
            match (i % 3, i % 5) {
                (0, 0) => println!("Fizzbuzz"),
                (0, _) => println!("Fizz"),
                (_, 0) => println!("Buzz"),
                _ => println!("{}", i),
            }
        }
    

\-- edited: removed `.to_string()`, thanks chrismorgan

~~~
kyllo
Weird to see this mix of a very imperative for-range iterative loop with a
very functional pattern match, which makes it look similar to an SML or OCaml
solution to FizzBuzz. I guess this is the definition of multi-paradigm right
here.

Will Rust's type checker warn you of a non-exhaustive pattern match?

~~~
icedog
Here's the same approach in F# without the different types of String;
therefore, easier to get more functional.

    
    
        let fizzbuzz num =     
           match num % 3, num % 5 with      
              | 0,0 -> "FizzBuzz"
              | 0,_ -> "Fizz"
              | _,0 -> "Buzz"
              | _,_ -> num.ToString()
    
        [1..100]
          |> List.map fizzbuzz
          |> List.iter (fun (s:string) -> printfn "%s" s)

~~~
throwaway283719
Haskell -

    
    
      fizzbuzz x = case (x `mod` 3, x `mod` 5) of
          (0, 0) -> "FizzBuzz"
          (0, _) -> "Fizz"
          (_, 0) -> "Buzz"
           _     -> show i
    
      mapM_ (putStrLn . fizzbuzz) [1..100]

~~~
drostie
Pattern matching is one of the ways to get "two-mod" code where the modulus
operator is used two times. For example, string concatenation and assignment
operators do the same in this Python code:

    
    
        for i in range(1, 101):
            x = ""
            if i % 3 == 0:
                x += "Fizz"
            if i % 5 == 0:
                x += "Buzz"
            if x == "":
                x += str(i)
            print(x)
    

If anybody is randomly curious it can be fun to solve FizzBuzz in Haskell the
same way that this Python code does it, but (to be more idiomatically Haskell)
storing the FizzBuzz success in a Maybe (or some other variable). If you
define the appropriate `~>`, and higher-precedence `|~`, and higher-precedence
`|~~>`, you can write the above function as:

    
    
        fizzbuzz x = 
            mod x 3 == 0    ~> "Fizz" 
            |~ mod x 5 == 0 ~> "Buzz" 
            |~~> show x 
    

It's interesting because it's sort of a "follow-through guards" situation; the
(|~) operator can at least be turned into a type signature of (Monoid m) =>
Maybe m -> Maybe m -> Maybe m.

------
wycats
For what it's worth, String in Rust is similar to StringBuffer in other
languages. You can append to a String; you can't append to a slice, which
always represents a fixed view.

A slice has storage that is borrowed from somewhere else, but it itself does
not have its own storage.

When you type `"foo"`, you are creating "static" storage (in the binary) and
the slice is borrowed from that fixed-position location in memory.

Mostly, your functions produce Strings and consume slices.

~~~
hrjet
But shouldn't they (String and slice) have some common super type to make this
all easy to _use_?

~~~
kibwen
I don't think a common ancestor is how you want to do this. Instead, I'd
encapsulate the common behavior in a trait (a.k.a. an interface, in other
languages), and then write functions that accept any parameters that implement
that trait. In Rust, you can do this even on "built-in" types like strings.
For an example, see the `print_me` function below, which operates on both
string types using a trait that I've defined myself.

    
    
      trait WhatIsThis {
          fn what_is_this(self);
      }
    
      impl WhatIsThis for String {
          fn what_is_this(self) {
              println!("'{:s}' is a string!", self);
          }
      }
    
      impl WhatIsThis for &'static str {
          fn what_is_this(self) {
              println!("'{:s}' is a string slice!", self);
          }
      }
    
      fn print_me<T>(me: T) where T: WhatIsThis {
          me.what_is_this();
      }
    
      fn main() {
          print_me("Blah blah blah");
          print_me("Yada yada yada".to_string());
      }

~~~
wycats
And I expect virtually all of the methods on slice to be available on String
before long. I think it's a bug that this isn't the case today.

It's already possible to have a function take "either a String or slice"
generically:

    
    
        def print_me<T>(me: T) where T: Str {
            println!("I am '{:s}'", me.as_slice());
        }
    

The overall ergonomics of this (or at least, the well-documented idioms) will
certainly improve in the coming months.

------
shurcooL
That was informative... and it reinforced my perception of Rust as something
to look into if I ever have to do something that absolutely necessitates the
use of something low level like C++/Assembly. But for everything else that I
can get away with (and that is a lot so far) I'll stick with Go because it's
so much faster and shorter to write.

~~~
retroencabulato
If something absolutely necessitates using C++, why not use C++? (I'm
genuinely asking.)

~~~
shurcooL
Because writing and compiling code/projects in it is very painful. Header
files, custom makefiles, etc.

~~~
frankzinger
Pardon me, but 'custom makefiles' have absolutely nothing to do with C++.
There are IDEs with C++ support.

Also, while working with headerless languages may be easier, calling working
with headers 'very painful' is hyperbole.

~~~
Tyr42
Irregardless of IDEs, I'd still need to learn how to use cmake and other
systems to build and use libraries. It's a large pain compared to `pip install
...`.

~~~
frankzinger
_...need to learn how to use cmake and other systems to build and use
libraries._

On OSes for which this is true you'd be forced to compile python yourself as
well, because it's also just a dependency (of pip, for one!) written in a
compiled language.

Edit: My point is: usually it's as easy as 'yum install', but on the rare
occasion you will need to compile, I admit. However, that could happen with
pip too; don't tell me its repos are always completely up to date. And in
those cases it won't be quite as simple as 'pip install'.

------
mihai_ionic
Here's what the final example from the blog would look like, if you were to
directly translate it into C++: [http://coliru.stacked-
crooked.com/a/265c6ec6fb6751a9](http://coliru.stacked-
crooked.com/a/265c6ec6fb6751a9)

Now, of course, no one would program that way, but I think it does help
visualize what really happens.

The obvious cost from delaying the printing is that you have to branch a
second time, later in the code, to consume the value. I wonder how feasible it
would be to introduce some kind of compiler transform that could invert the
control flow, essentially pasting the surrounding code into the inner
branches, to make this abstraction cost-free.

------
blt
"may not _compile_ " would be a better title than "may not work". To a
programmer familiar with compiled languages, "may not work" implies that the
code will compile but produce the wrong result/a crash/undefined behavior.
Rust aims to catch mistakes at compile time, so the headline is quite
sensational under this interpretation.

------
Padding
> There is a trade‐off here for them; as a general rule, such languages have
> immutable strings

This is a bit disingenuous. I understand what he's aiming at, and he also
mentions StringBuilder later on, but saying that a GC necessatites immutable
strings is simply not true.

As a counterexample: PHP has mutable strings and uses copy-on-write in
situations where it "feels" that conflicts could occur. (Granted PHPs rules on
how it handles its variables is a bit arbitrary and magical, and PHP didn't
have a GC till version 5.3 .. but the argument still stands.)

~~~
chrismorgan
Yes, I know I was being rather fuzzy there. I was intending to give a general
impression of where the differences lie.

------
Hello71

        for i in range(1, 101):
            print('FizzBuzz' if i % 15 == 0 else 'Buzz' if i % 5 == 0 else 'Fizz' if i % 3 == 0 else i)
    

python doesn't have expressions? oh dear me.

~~~
burntsushi
The OP's characterization is reasonably accurate _in my experience_. I've run
into several Python programmers who didn't actually know about Python's
special "if expression" syntax.

Moreover, the code you've presented here certainly isn't idiomatic, which
counts for something.

~~~
Hello71
sure, but when you're implicitly comparing code segments (by placing them next
to each other), you should at least make the effort to make them more the
same, instead of pointing out that one language is missing a feature used in
the other language, especially when this claim is false.

the formatting can of course be improved:

    
    
        for i in range(1, 101):
            print('FizzBuzz' if i % 15 == 0 else
                  'Buzz' if i % 5 == 0 else
                  'Fizz' if i % 3 == 0 else
                  i)
    

there's also a suspicious "return" at the end of the second code segment which
mysteriously appeared some time after the first one; looks like the author was
trying a little too hard to differentiate python and rust.

~~~
burntsushi
I disagree. I think code comparisons should be done using _idiomatic_ code. I
personally would not consider chaining `if` expressions like you've done here
idiomatic Python.

~~~
Hello71
and yet people write:

    
    
        let result = if i % 15 == 0 {
            "FizzBuzz"
        } else if i % 5 == 0 {
            "Buzz"
        } else if i % 3 == 0 {
            "Fizz"
        } else {
            i
        };
    

in rust? either this is good, readable code or this is poorly written,
unintelligible code. you cannot make the argument that sometimes it is
readable and sometimes not based on the presence of braces.

~~~
dbaupp
The comparison is not "readable", it is "idiomatic". (I'm not saying either is
unreadable, just pointing out that you're attacking the wrong thing.)

------
lmm
Scala guy here. This kind of thing is useful - I'm happy to pay the costs of
garbage collection so I don't need it for ownership, but for separating out
async operations from sync operations, or database-transactional operations
from non-database operations, it's great to be able to represent that
difference in the type system (and without a huge syntactic overhead). But
then if you want to abstract over types like MaybeOwned, you need higher-
kinded types to be able to work with them effectively. Has Rust got any
further towards implementing those?

~~~
mhaymo
Unfortunately I don't think higher-kinded types are planned for Rust 1.0. See
the rfc: [https://github.com/rust-
lang/rfcs/issues/324](https://github.com/rust-lang/rfcs/issues/324) and this
HN thread:
[https://news.ycombinator.com/item?id=7997926](https://news.ycombinator.com/item?id=7997926)

~~~
Argorak
They plan an aggressive release schedule, though:

[http://blog.rust-
lang.org/2014/09/15/Rust-1.0.html#release-p...](http://blog.rust-
lang.org/2014/09/15/Rust-1.0.html#release-process)

So they could come in one of the post 1.0 releases and that could possibly be
soon.

------
michel-slm
While FizzBuzz is a low-pass filter in most languages, with Rust it makes for
a fascinating window into the language design!

~~~
JBiserkov
... and therefore a viable interview question :)

------
Robadob
I've always liked this c++ implementation of FizzBuzz, its not the most clear
or logical but its short;

    
    
        const char* outs[] = { "%d\n", "Fizz\n", "Buzz\n", "FizzBuzz\n" };
        for (int i = 1; i < 101; i++)
            printf(outs[((((i%5)==0)<<1)+((i%3)==0))], i);

~~~
heinrich5991
Is that allowed by the standard? (Passing a parameter to `printf` but not
referencing it, in the non-"%d\n" case?)

~~~
kps
C11 §7.21.6.1

    
    
      If the format is exhausted while arguments remain, the excess
      arguments are evaluated (as always) but are otherwise ignored.
    

but that's just a consequence of stdarg, which does not require all supplied
arguments to be consumed.

------
donatj
The meandering run-on garden path sentences make this post very hard to follow
at times.

~~~
chrismorgan
Yeah, I’m afraid I do have a bit of a tendency to letting that happen.

------
dhon_
In the contrived Python example, FizzBuzzItem is only called if the result is
a number (not in any of the modulo 0 cases) - is that intended? I can see that
it works, but it's breaking the analogy for me with the Rust code.

~~~
chrismorgan
Whoa, that was indeed a mistake. Sorry about that. Fixed.

~~~
dhon_
Thanks, also is the "try it" link on rust playpen linking to the wrong code?

~~~
chrismorgan
No, that’s the code that is supposed to be there. It’s indicating that there
is nothing that you can put as the lifetime there.

------
jerf
You know Rust team, it almost might be worth specializing this exact error
message about mismatched string lifetimes to include a URL to this post, if
not mismatched lifetimes in general.

You _know_ it's going to be a FAQ....

~~~
steveklabnik
We have unique diagnostic codes for each error, and I have plans (and an in-
progress PR) that points to a web page with a much longer "this is what this
error looks like, here are some strategies with how to fix it" in the works.

------
GhotiFish
The feature list in rust really does have my eye. The biggest one in
particular was _type inference_.

The _reason_ type inference was such a big one was because if you use it
right, annoying situations like "Two types of strings? What is this?" go the
hell away. You have three types, static built in and binary strings, and a
third that only makes the gaurentee that the datatype can do all the things a
string aught to be able to do, and from there the compiler works out the
practical implementations.

This article has done a great job in killing my enthusiasm for the language.

I guess it's implementation of type inference only goes as far as golangs, in
that const keyword.

Maybe I was being a bit naive in what I was expecting, hell maybe what I'm
expecting isn't reasonably possible. bleh.

~~~
dbaupp
Rust has type inference similar to Haskell: type information can flow
"backwards". It is very different to Go and C++ where types of locals are
'inferred' from their initialiser, and nothing else.

E.g.

    
    
      fn main() {
          let mut v;
    
          if true {
              v = vec![];
              v.push("foo");
          }
      }
    

is a valid Rust program: the compiler can infer that `v` must have type
`Vec<&str>` based on how it is used. I don't think it's possible to
syntactically write a Go program at all similar to this (a variable has to be
either initialised or have a type), and the C++ equivalent (using `auto v;`)
is rejected, as `auto` variables need an initialiser.

~~~
mercurial
Doesn't type inference stop at function boundaries, though? I'll grant you
that idiomatic Haskell uses type annotations for function signatures (unlike
idiomatic OCaml), but it is optional (which is convenient in a REPL).

~~~
steveklabnik
Yes, it does, but that's a deliberate design decision, not a flaw. We decided
enforcing this idiom was a good idea.

~~~
mercurial
I think it was related to the quality of error messages as well.

~~~
steveklabnik
Right. When you infer signatures, a change in one place can cause an error
somewhere else, and so the error message is very misleading.

------
aftbit
What's up with the "alternative" form of Python? What about braces,
semicolons, and an explicit main() function makes that version worth showing?

~~~
TillE
Well, "from __future__ import braces" is a joke, so applying the transitive
property of jokes, I assume this alternative version is a joke as well.

~~~
chrismorgan
Indeed it is. And people should certainly try executing `from __future__
import braces` if they’re not familiar with what it is.

As for the main function and `if __name__ == '__main__': main()`, that part
_is_ actually generally recommended.

