DEV Community

Cover image for Learning Rust ๐Ÿฆ€: 03- The basics: Functions
Fady GA ๐Ÿ˜Ž
Fady GA ๐Ÿ˜Ž

Posted on • Updated on

Learning Rust ๐Ÿฆ€: 03- The basics: Functions

I'll continue my Rust Learning Series and this time I'll continue talking about the basics, more specifically, functions. But first, I'd like to bring up a very good VS Code extension to work with Rust, the "rust-analyzer"

โš ๏ธ Remember!

You can find all the code snippets for this series in its accompanying repo

If you don't want to install Rust locally, you can play with all the code of this series in the official Rust Playground that can be found on its official page. (cargo commands won't run though. But you mostly won't be needing it)
โš ๏ธโš ๏ธ The articles in this series are loosely following the contents of "The Rust Programming Language, 2nd Edition" by Steve Klabnik and Carol Nichols in a way that reflects my understanding from a Python developer's perspective.

โญ I try to publish a new article every week (maybe more if the Rust gods ๐Ÿ™Œ are generous ๐Ÿ˜) so stay tuned ๐Ÿ˜‰. I'll be posting "new articles updates" on my LinkedIn.

Table of Content

The rust-analyzer VS Code extension:

Rust is hard to learn, I get it. But the good news is that there are tools out there that can make it easier! ๐Ÿ˜‰
If you are using VS Code, rust-analyser is a wonderful companion to your Rust learning journey ๐Ÿ‘Œ. I highly recommend installing this extension while learning Rust (and when you are done learning too). As listed in its manual, rust-analyzer "is a library for semantic analysis of Rust code as it changes over time." Think of it as your Rust peer developer that has your good Rust coding form in heart ๐Ÿฅฐ. It can do a lot of stuff, like:

  • Code completion with imports insertion
  • Go to definition, implementation, type definition
  • Find all references, workspace symbol search, symbol renaming
  • Types and documentation on hover
  • Inlay hints for types and parameter names
  • Semantic syntax highlighting
  • A lot of assists (code actions)
  • Apply suggestions from errors
  • And many more...

Highly recommended ๐Ÿ‘Œ

Functions:

You may not know it, but you know how to define a function in Rust by now ๐Ÿ˜Š. Remember the main function?!
Function in Rust are defined with the fn keyword. Like Python, functions can be called from other functions:

fn main() {
    println!("Main Function");
    // A function call
    second_function();

fn second_function() {
    println!("Second Function");
}
Enter fullscreen mode Exit fullscreen mode

Here, we called second_function from the main function and the output should be like this:

Compiling functions v0.1.0 (/mnt/c/Users/fady/code/03-the-basics-functions-and-flow-control/functions)
    Finished dev [unoptimized + debuginfo] target(s) in 1.17s
     Running `target/debug/functions`
Main Function
Second Function
Enter fullscreen mode Exit fullscreen mode

Parameters:

Like other languages, functions in rust accept parameters, arguments or whatever what you call the values passed to it ๐Ÿคทโ€โ™‚๏ธ. But in Rust, parameters must have a defined type to eliminate the guess work for the Rust compiler.

fn main() {
    // A function with parameters
    print_temperature(35, 'C');
}
fn print_temperature(value: i32, unit: char) {
    println!("The temperature is {value}{unit}");
}
Enter fullscreen mode Exit fullscreen mode

The output this time will be:

The temperature is 35C
Enter fullscreen mode Exit fullscreen mode

Statements and Expressions:

While in Python land, I didn't even bother to know the distinction between statements and expressions, who cares?! ๐Ÿ˜
But in Rust, you have to know the difference and you will see why in the next section.
In short, "Statements" don't return a value and "Expressions" do and you can turn any expression into a statement by terminating it with a semi-colon ";", that's it! ๐Ÿ˜‰
(Technically, statements return the "unit" aka THE VOID ๐Ÿ˜ฏ)

Here is an example:

fn main() {
    // Statements and Expressions
    // let y = (let z = 10); // Error: expected expression, found statement
    let x = 10; // Statement
    println!("The values of x is {x}");

    let y = {
        let z = 10;
        z + 13
    }; // The whole block is an expression
    println!("The value of y is {y}");
}
Enter fullscreen mode Exit fullscreen mode

The let statement is a classic example of a Rust ... well ... statement ๐Ÿ˜. It doesn't return anything. So the following would error out let y = (let z = 10); as (let z = 10) is a statement and you can't assign statements to statements! This makes my brain hurt ๐Ÿค•!
For y, notice the absence of the semi-colon ";" after z + 13 in the block. This (as we will see next) is the return value of the block and the whole block becomes an expression as it produces a "value". When you execute this code, you will see that the value of y is 23.

Return values:

You've probably guessed it from the previous section ๐Ÿ˜‰. Functions can have explicit return value which is the "last expression in the function block". But like parameters, you must define the type of the function's return value. We use a similar syntax to Python's "type hinting". To define the return's value type, we use -> then the desired type.
Here is an example:

fn main() {
    //Return Values
    let number_five = five();
    println!("number_five's value is {number_five}");

    let add_one = add_one(7);
    println!("The value of add_one is {add_one}")
}
fn five() -> u8 {
    5
}

fn add_one(value: i32) -> i32 {
    // value + 1; // Error (notice the semi-colon). The function is expecting a i32 return type but got unit ()
    value + 1
}
Enter fullscreen mode Exit fullscreen mode

For the five() function, we return the number 5 (notice the absence of the semi-colon which makes this an expression). 5 can be safely casted into an "8-bits unsigned integer" type, hence the -> u8 after the parentheses. If we rewrite the five() function as the following, it will error out:

fn five() -> u8 {
    5; // adding a semi-colon.
}
Enter fullscreen mode Exit fullscreen mode

Here we are telling the Rust compiler that we are expecting an "8-bit unsigned integer" but the semi-colon transformed the last expression in the function's block into a statement, hence this function now returns "the unit ()".

That's a quick overview on Functions in Rust! In the next article, we will continue our discussion about flow control (if statement and loops). See you then ๐Ÿ‘‹

Top comments (0)