Introduction - 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 nightlies 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 already1. 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.

1

This is a programmer specified conversion which uses the Display 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.

TODO check this footnote hack works