Friday, April 18, 2014

Rust for C++ programmers - part 1: Hello world

This is the first in a series of blog posts (none written yet) which aim to help experienced C++ programmers learn Rust. Expect updates to be sporadic at best. In this first blog post we'll just get setup and do a few super basic things. Much better resources are at the tutorial and reference manual.

First you need to install Rust. You can download a nightly build from http://www.rust-lang.org/install.html (I recommend the nighlties rather than 'stable' versions - the nightlies are stable in that they won't crash too much (no more than the stable versions) and you're going to have to get used to Rust evolving under you sooner or later anyway). Assuming you manage to install things properly, you should then have a `rustc` command available to you. Test it with `rustc -v`.

Now for our first program. Create a file, copy and paste the following into it and save it as `hello.rs` or something equally imaginative.
fn main() {
    println!("Hello world!");
}
Compile this using `rustc hello.rs`, and then run `./hello`. It should display the expected greeting \o/

Two compiler options you should know are `-o ex_name` to specify the name of the executable and `-g` to output debug info; you can then debug as expected using gdb or lldb, etc. Use `-h` to show other options.

OK, back to the code. A few interesting points - we use `fn` to define a function or method. `main()` is the default entry point for our programs (we'll leave program args for later). There are no separate declarations or header files as with C++. `println!` is Rust's equivalent of printf. The `!` means that it is a macro, for now you can just treat it like a regular function. A subset of the standard library is available without needing to be explicitly imported/included (we'll talk about that later). The `println!` macros is included as part of that subset.

Lets change our example a little bit:
fn main() {
    let world = "world";
    println!("Hello {}!", world);
}
`let` is used to introduce a variable, world is the variable name and it is a string (technically the type is `&'static str`, but more on that in a later post). We don't need to specify the type, it will be inferred for us.

Using `{}` in the `println!` statement is like using `%s` in printf. In fact, it is a bit more general than that because Rust will try to convert the variable to a string if it is not one already*. You can easily play around with this sort of thing - try multiple strings and using numbers (integer and float literals will work).

If you like, you can explicitly give the type of `world`:
    let world: &'static str = "world";
In C++ we write `T x` to declare a variable `x` with type `T`. In Rust we write `x: T`, whether in `let` statements or function signatures, etc. Mostly we omit explicit types in `let` statements, but they are required for function arguments. Lets add another function to see it work:
fn foo(_x: &'static str) -> &'static str {
    "world"
}

fn main() {
    println!("Hello {}!", foo("bar"));
}
The function `foo` has a single argument `_x` which is a string literal (we pass it "bar" from `main`). We don't actually use that argument in `foo`. Usually, Rust will warn us about this. By prefixing the argument name with `_` we avoid these warnings. In fact, we don't need to name the argument at all, we could just use `_`.

The return type for a function is given after `->`. If the function doesn't return anything (a void function in C++), we don't need to give a return type at all (as in `main`). If you want to be super-explicit, you can write `-> ()`, `()` is the void type in Rust. `foo` returns a string literal.

You don't need the `return` keyword in Rust, if the last expression in a function body (or any other body, we'll see more of this later) is not finished with a semicolon, then it is the return value. So `foo` will always return "world". The `return` keyword still exists so we can do early returns. You can replace `"world"` with `return "world";` and it will have the same effect.



* This is a programmer specified conversion which uses the `Show` trait, which works a bit like `toString` in Java. You can also use `{:?}` which gives a compiler generated representation which is sometimes useful for debugging. As with printf, there are many other options.

11 comments:

Anonymous said...

When you add *notes can you please link from them back to the point in the post they refer to, and vice versa?
We've been spoiled by wikipedia doing that automatically :)

Daniel Glazman said...

Hi Nick. Thanks for starting that series of article, I think it's highly welcome! But I recommend you don't introduce more than one notion at a time. Here, the "&'static" is enough for a very long article in itself and is adding confusion and questions. You said yourself that you'll come back to that later but it's already mentioned 3 times...
One other thing: the _ prefix to avoid warnings really feels like a hack... I understand the use case and interest, but that kind of things that impose restrictions on the variable namespace seem to me unreasonable. My 0.02€...

Nick Cameron. said...

@Anonymous: yeah, I should do that. I'll try to figure out how to do it...

Nick Cameron. said...

@Daniel: Ah, I felt bad about &'static as I typed it - I really wanted to leave types till later, especially these awful looking ones, but I also wanted to do something with strings, hey-ho.

As for _ prefix, I also hated it when I started learning Rust. I've grown to like it a bit though - it fits nicely with using _ for an un-named variable, and I can't think of a better way to turn off the unused variable warning. I don't think it restricts the variable namespace, you can still use variables beginning with _ and name them. So it is more of a convention which the compiler takes advantage of when choosing whether to warn or not.

njn said...

I'm used to _ prefixes from old days programming Prolog, where they have a similar meaning. In my experience it works well.

Ryan said...

These posts are quite helpful! Some parts of the official Rust tutorial are pretty confusing currently, as there are just random thoughts strewn about for some sections, so it's pretty hard to read.

Excited to see more of the series!

glandium said...

Does the println syntax allow permutation of the arguments? Something equivalent to:
printf("%2$s $1$d")
?
This is crucial for l10n.

Nick Cameron. said...

@Glandium - yes. This probably deserves a post to itself, but the docs are good - http://static.rust-lang.org/doc/master/std/fmt/index.html

Anonymous said...

Are programmers really so lazy that we must make software error prone and confusing so that we don't have to type 'return x;'? If I come from another language like python or ruby, I might not remember to place semicolons at the end of lines and thus unintentionally return when I don't really mean to. IMHO this sort of silliness is a kin to semicolon insertions in javascript, and is just a bad idea.

Also what is the reason for making parens around conditions optional? IMHO this is just more laziness that is ripe for creating confusion.

Anonymous said...

The trailing semicolon is significant and gives you compile time errors in the case of mistakes - this is not an error prone feature like in javascript. It simply makes more sense with an expression oriented syntax. The expression oriented nature of rust is LESS error prone than c++, because it avoids uninitialised values or spaghetti logic. The lack of parens atound conditionals is just a syntax optimization, because a set of braces is compulsory, the syntax is unambiguous without extra parens around the condition - again, it is c++ that makes the error prone choice with optional *braces*

James Daniel said...

I disagree that the semicolon thing is a good idea, but in the other direction. If you want the unit type, you should just to return that.

It's a shame, because I quite like a lot of what rust does, but this seems like a hacky compromise and it's very annoying.