Syntax
Don't jump ahead!
Complete the exercise for the previous section before you start this one.
It's located in exercises/01_intro/00_welcome
, in the course GitHub's repository.
Use wr
to start the course and verify your solutions.
The previous task doesn't even qualify as an exercise, but it already exposed you to quite a bit of Rust syntax.
We won't cover every single detail of Rust's syntax used in the previous exercise.
Instead, we'll cover just enough to keep going without getting stuck in the details.
One step at a time!
Comments
You can use //
for single-line comments:
#![allow(unused)] fn main() { // This is a single-line comment // Followed by another single-line comment }
Functions
Functions in Rust are defined using the fn
keyword, followed by the function's name, its input parameters, and its
return type.
The function's body is enclosed in curly braces {}
.
In previous exercise, you saw the greeting
function:
#![allow(unused)] fn main() { // `fn` <function_name> ( <input params> ) -> <return_type> { <body> } fn greeting() -> &'static str { // TODO: fix me π "I'm ready to __!" } }
greeting
has no input parameters and returns a reference to a string slice (&'static str
).
Return type
The return type can be omitted from the signature if the function doesn't return anything (i.e. if it returns ()
,
Rust's unit type).
That's what happened with the test_welcome
function:
#![allow(unused)] fn main() { fn test_welcome() { assert_eq!(greeting(), "I'm ready to learn Rust!"); } }
The above is equivalent to:
#![allow(unused)] fn main() { // Spelling out the unit return type explicitly // π fn test_welcome() -> () { assert_eq!(greeting(), "I'm ready to learn Rust!"); } }
Returning values
The last expression in a function is implicitly returned:
#![allow(unused)] fn main() { fn greeting() -> &'static str { // This is the last expression in the function // Therefore its value is returned by `greeting` "I'm ready to learn Rust!" } }
You can also use the return
keyword to return a value early:
#![allow(unused)] fn main() { fn greeting() -> &'static str { // Notice the semicolon at the end of the line! return "I'm ready to learn Rust!"; } }
It is considered idiomatic to omit the return
keyword when possible.
Input parameters
Input parameters are declared inside the parentheses ()
that follow the function's name.
Each parameter is declared with its name, followed by a colon :
, followed by its type.
For example, the greet
function below takes a name
parameter of type &str
(a "string slice"):
#![allow(unused)] fn main() { // An input parameter // π fn greet(name: &str) -> String { format!("Hello, {}!", name) } }
If there are multiple input parameters, they must be separated with commas.
Type annotations
Since we've been mentioned "types" a few times, let's state it clearly: Rust is a statically typed language.
Every single value in Rust has a type and that type must be known to the compiler at compile-time.
Types are a form of static analysis.
You can think of a type as a tag that the compiler attaches to every value in your program. Depending on the
tag, the compiler can enforce different rulesβe.g. you can't add a string to a number, but you can add two numbers
together.
If leveraged correctly, types can prevent whole classes of runtime bugs.
Exercise
The exercise for this section is located in 01_intro/01_syntax