Introduction
Hello again!
In the last post we’ve set up our environment to write some Rust code. Today we will finally see some stuff happening!
That’s enough, let’s see some code
As expected, let’s begin with last post’s Hello World application
fn main() {
println!("Hello World!");
}
… And that’s it.
Rust is heavily inspired by functional languages such as Ocaml, so lets learn how to write some functions!
Functions
The language follows most of the common conventions of C based languages, with a nicer syntax in some places inspired by functional languages (especially Ocaml). For instance, as with C you can declare function, though you need to put the return type after the function name and parameter names:
fn meaning(x: bool) -> i32 {
//…
}
But different from C when returning a value you can omit the “return” keyword and the last statement will be returned, such as in:
fn meaning(x: bool) -> i32 {
42
}
This gets really interesting because, for instance, if
is a statement. Therefore, what you might write as such in a C derived language
int meaning(bool x) {
if (x) {
return 42;
} else {
return 0;
}
}
can be written as such in Rust
meaning(x: bool) -> i32 {
if x {
42
} else {
0
}
}
meaning(true) // returns 42
This contrived example only shows a minor improvement, but it comes in handy on more complex scenarios and makes code using match statements (which will be shown in a future post) nicer to read. Something else of note is that comments start with //
and goes until the end of the line. Rust does not have the concept of a block comment, therefore all lines of comment must start with //
. In a future post we will talk about how to use comments to add documentation to your code.
If your function does not return any useful value it is said to return ()
, which is a Unit
type
fn useless() -> () {
println!("I'm useless!")
}
You can think of it as the void
type in other languages. To make things simpler you can omit the return type for functions that return ()
fn useless() {
println!("I'm useless!")
}
Statements
A statement is a component inside a block of code. Pretty much everything in Rust is a statement, including other blocks, making the definition somewhat recursive. Statements that finish with a ;
return the Unit
type. Like it was mentioned before, the last statement in a function will be the returned value of that function. This can lead to somewhat interesting constructs.
fn foo() -> i32 {
40
}
fn bar() -> i32 {
let foo_plus_one = {
let foo_result = foo(); //the whole statement let foo_result = foo(); has type () because it ends with a ;
foo_result + 1
};
foo_plus_one + 1 // if ; was added here, this statement would be of type (), which doesn't match the function's signature
}
println!(bar()) // prints 42
This pattern is useful once we get into the ownership model of Rust, which will be talked about in a future post. The keen reader might have noticed in the code sample the call to println
includes a “!”, while the call to foo
and bar
does not. This is because “println” is not a function but a macro. Depending on how much experience you have in C and C++ you might just have had flashbacks of some interesting bug hunting. The macro system in Rust is smarter and safer than those, so you don’t need to be worried! We will also be the topic of a future post.
Declaring variables
In the latest code sample we can see the declaration of too variables, foo_plus_one
and foo_result
. By default variables in rust are immutable. The following code
let x = 5;
x = 6;
Will result in the following compiler error
error[E0384]: cannot assign twice to immutable variable `x`
→ src/main.rs:3:3
|
2 | let x = 5;
| -
| |
| first assignment to `x`
| help: make this binding mutable: `mut x`
3 | x = 6;
| ^^^^^ cannot assign twice to immutable variable
To make a variable mutable we introduce the mut
keyword.
let mut x = 5;
x = 6; // this works fine
The control of mutability is a core concept in Rust and will be talked about in a future post. Something else the reader might have noticed is the lack of a type declaration in the variable. This does not mean that Rust is not a typed language! Rust is strongly typed, but types can be inferred most of the time. If you want to be explicit about the type of a variable you can introduce the type information in the function declaration:
let x: i32 = 5;
There are few places where you must define the type of the variables. The most common ones are the function’s return type and parameter types
fn foo(x: i32, y: i32) -> bool {
…
}
What’s next?
I hope this first post was helpful as a basic introduction to the language’s syntax and building blocks. On the next post I’ll talk about the primitive types in rust, declaring your own types and how to handle control flow in the language.
See you then!