Is the homepage example wrong on purpose?


#1

We all know the homepage example, but I think that it doesn’t serve Rust in a good way.

The problem with that example is in the arithmetic and the type used for the computation, that example shows you that Rust is unable to pick a good guess for the right type given a set of operation on that type if the user doesn’t explicitlily picks a type for numerical computations .

I think that the example should be

// This code is editable and runnable!
fn main() {
    // A simple integer calculator:
    // `+` or `-` means add or subtract by 1
    // `*` or `/` means multiply or divide by 2

    let program = "+ + * - /";
    let mut accumulator = 0f64;

    for token in program.chars() {
        match token {
            '+' => accumulator += 1f64,
            '-' => accumulator -= 1f64,
            '*' => accumulator *= 2f64,
            '/' => accumulator /= 2f64,
            _ => { /* ignore everything else */ }
        }
    }

    println!("The program \"{}\" calculates the value {}",
              program, accumulator);
}

Also note that the rustc compiler is also unable to warn you about the truncation happening in the original example.

The original example for historical purposes

// This code is editable and runnable!
fn main() {
    // A simple integer calculator:
    // `+` or `-` means add or subtract by 1
    // `*` or `/` means multiply or divide by 2

    let program = "+ + * - /";
    let mut accumulator = 0;

    for token in program.chars() {
        match token {
            '+' => accumulator += 1,
            '-' => accumulator -= 1,
            '*' => accumulator *= 2,
            '/' => accumulator /= 2,
            _ => { /* ignore everything else */ }
        }
    }

    println!("The program \"{}\" calculates the value {}",
              program, accumulator);
}

#2

Rust doesn’t guess which type you want. Either it is inferred from a type requirement, or it falls back to i32


#3

To clarify, what exactly are you thinking is “wrong” about the current example? That the division by 2 truncates?

Finding a succinct example that displays some nice features of Rust is definitely tricky. The example does say A simple integer calculator:, so the truncating division doesn’t seem too problematic (if that is what you are referring to). In any case, creating an improved example would be great, I’m interested to hear what others have to say about your proposal

(BTW, you can improve your proposal by using 0.0, 1.0, 2.0 literals instead of repeating the f64 suffix everywhere.)


#4

well, if you put it this way, type inference is not the strongest suit for Rust then, especially for numerical types/computations .


#5

My point is that that examples shows the weakness of Rust in several aspects

  • type inference
  • numerical computations
  • compiler features

and maybe we should also discuss why i32 is the default when a double can be a good candidate for both floating point computation and integers without any substantial difference in terms of cost on modern CPUs .

rustc can make suggestions about how I write my functions, the semantics of my functions, and can’t be better at this ?

I can’t really help at writing an example that will be representative for the Rust language since I’m still learning it, but the match construct looks peculiar enough to deserve an inclusion, also I think that it’s probably time to focus on multithreading and concurrent safe data structures for the introduction to a new language .


#6

Hm, I’m still unclear about what exactly the problem/weakness is: is it the truncating division of accumulator /= 2?


#7

Rust has already decided that 0, 1, 2 are integer literals, the float types are not candidates for inference here.


#8

A better home page example is welcome. Many people have noticed the weaknesses of the current, but no alternatives have appeared yet, which kind of surprises me since being the author of the prime example of Rust is surely worth some karma.


#9

@huon I’m sorta speculating but this seems to be his issue as I can understand it. I’d wait for his confirmation though.

@prok If this worked, and the accumulator was set to 0f64 or 0.0, would that fix your issue?

let mut x = 0f64;
x += 1;

If I’m correct, I’d say part of the complaint is:

  • If x is f64 then
  • 1 should be inferred as f64 and
  • it shouldn’t be required to annotate as 1.0 or 1f64 in this case

Then, the home page example should similarly automatically change type based on what the original accumulator type starts at.

[EDIT] The bugs related to improving the error message and better inference


#10

@brson I have some ideas for a new homepage code snippet, but I’m a little worried about whether I might exceed our code length and/or complexity budget. Do you have any rough guidelines here, or should I just skim over the previous versions of the homepage to get an idea of what code length has been reasonable in the past?

(My current candidate code is a topological sort program; my code with an example invocation and a few comments is currently 66 lines; I’m hoping I can cut that down a bit more…)


#11

I’ve been thinking about what would make a good homepage example, and the more I think about it, the more I find it hard to imagine a single example that would be both short enough, and actually do justice to the language. I think that having a few different choices, either randomly selected or with some way to navigate between them, might be better than trying to have one single example.

I decided to do a poll of other languages, to see what kinds of front-page examples they have in order to be able to compare their effectiveness at expressing what the language is all about.

  • C, C++, C#, Objective-C, Java, JavaScript, Common Lisp, Scheme, Fortran, COBOL, Visual Basic, Visual Basic .NET, Delphi, Pascal, APL, Prolog, PowerShell, Bourne shell don’t really have any kind of official landing page or obvious top community landing page (at least, that I could find in the top few hits on Google).
  • SML/NJ, F#, Clojure, Perl, PHP, Ada, R, Matlab, J, K, Mozart, Lua, Groovy, Processing, Smalltalk, Squeak, Inform7, Self, Vala, Io, Coq have landing pages, but no code examples on them.
  • Ruby has a randomly selected example from a set of 4 (at least, that I saw after several reloads), each of which is 10-15 lines long. One is a one-liner “Hello, World” with 9 lines of comment describing how nice it is that it’s only a single line, one contains a few basic string manipulation, iteration, and printing examples, one demonstrates set difference on word arrays, and one demonstrates a very basic “Hello, World” class.
  • Python has a slideshow with 5 examples, each 10 lines or fewer and with a prose description of what it is demonstrating. It’s examples are Fibonacci, list comprehensions and enumerate, basic arithmetic, basic print and input I/O, and a for loop. The slideshow autoplays with no pause button, making it frustrating if you want to think about one of the examples a little longer than the delay.
  • Scala has no code example immediately visible on the front page, but each of the major features listed in the “Scala in a Nutshell” section expands to an example of that feature. It covers Java interop, type inference, concurrency via future, traits, pattern matching, and higher-order functions. They range in length from 5 to about 30 lines.
  • Haskell has a prime sieve in 3 lines, plus a REPL with a tutorial that you can play with. If you copy the prime sieve into the REPL, it fails to parse, which is somewhat disappointing. The tutorial is nice, though a bit chatty so you have to spend a while going through things like “you can run sort on a string” before getting to more interesting topics.
  • Ocaml has a 12 line example of a binary tree data type with an exists_leaf method that walks the tree and applies a predicate to each leave, and a has_even_leaf function that builds on that to find leaves with even values, along with a link to a page with a few more examples of a square function, factorial, sorting a list, and so on.
  • Go allows you to select between several examples, with a 9 line Unicode “Hello, 世界” as the default example. All of the other examples are too big to fit in the size of the box for the “Hello, 世界” example, so they are kind of hard to read. They are: Conway’s Game of Life, a function that returns a closure that when invoked repeatedly generates the Fibonacci sequence, using Peano integers to compute factorial to demonstrate that segmented stacks help with deep recursion, Pi computed via a number of goroutines, a concurrent prime sieve, a peg solitaire solver, and tree comparison done by two goroutines traversing two different trees and sending results to a third to compare.
  • Julia has an 11 line Mandelbrot program, 16 line statistics example applied to random data, and a 3 line parallel coin flip example.
  • Erlang doesn’t have a homepage example, but does have a quickstart that shows basic REPL usage and a factorial function
  • D has a 16 line example that computes the average line length of input provided on stdin.
  • Swift has a screenshot of the development environment with small but readable (at least on a high-DPI display) text that appears to be an excerpt from a game, as well as a 3 line example of sorting an array based on a custom closure.
  • Nim has an auto-playing slideshow with five examples on three slides; one of safe data-parallel code, one of averaging the length of lines on stdin, one defining a Person type and hello world operation on it, one demonstrating basic C FFI, and one with an example of a simple lightweight router-based (Sinatra/Flask style) web app. All are under 15 lines.
  • CoffeeScript demonstrates a number of its sugars and corresponding JavaScript, but nothing that is a full working program
  • Dart has three examples, that you can navigate between (plus a diagram of how packages work). One is a simple die roller class, that demonstrates a few language features like classes, exceptions, optional parameters, etc. The next shows a few more sugars. The last is a snippet demonstrating optional typing.
  • Typescript contains one example showing a class and that types allow it to highlight a typo in a method name.
  • Crystal has an 18 line Sieve of Eratosthenes.
  • Elixir has a few examples interspersed with prose about its benefits, all about 10 lines or fewer; it has “Hello, World” that sends a message between processes (green threads), an example that demonstrates starting a supervisor process and child processes (though I’m not sure it would make much sense unless you know about Erlang’s OTP already), some examples of destructuring bind and guards, an example of a unit test DSL, some examples of their package manager and REPL, and an example of calling an Erlang library.
  • Racket has a number of examples that you can choose between, all 7 lines and under including the lang declaration. They are a recursive directory search via regex, a “Hello, World” web server, a TCP echo server, a unique filter, Sierpinski’s triangle in their picture language, a GUI guessing game, a Google scraper, a command-line dice roller, printing the Greek alphabet to demonstrate Unicode support, an interactive animation, a lazy implementation of Fibonacci, an example of occurence typing with union types, an example of an alternate syntax that works as a markup language, a mathematical plotting example, an example of sending email, an FFI example calling the WinMM API, and an example of Datalog.
  • Haxe has a 12 line example of iterating over the keys and values in a hash table and printing the results.

Out of this set, I think I like Scala’s examples the best, followed by Nim’s, though I really didn’t like the autoplaying slideshow (for Nim or Python). Scala does a good example of highlighting the features that distinguish it from other languages; yes, you can implement Fibonacci or iterate over a list in any language, that’s not all that interesting, show me a short and sweet example that motivates why I should use this language over any other; in Scala’s case, that’s Java interop, the type system, traits, concurrency, and so on, all of which they had examples of, in Nim’s case, it’s the C FFI, safe (ish) parallelism, convenient DSLs, and compact syntax, all of which they have examples of.

I found that I had a tendency to stop paying attention to any example that was more than 30 lines long (Go was particularly bad here; the examples didn’t fit in the provided space at all, causing them to wrap and scroll, and had very verbose examples). The one-liners tended to be a little less interesting, as did the longer examples; 10 to 20 lines is probably about the sweet spot for examples, but the short little 5 line or slightly more involved 30 line examples could be interesting too.

So, I would try to come up with a few examples, each highlighting one or more features, with one displayed up front but the ability to switch between a few of them to get an overview of more features. I think that at least one example demonstrating the safety of the borrow checker, by having an example that doesn’t work at first but which has a comment indicating how to fix the problem and have it work properly, would be ideal.


#12

@pnkfelix My only guideline is that I want the entire homepage to fit on my screen.


#13

Nice legwork, @lambda! Thanks a lot.


#14

This topic came up again on /r/rust, so I’m just bumping this because @lambda’s post was so good.