Rust Basics Series #5: Functions in the Rust Programming Language
In this chapter of the Rust Basics series, learn to use functions and return values from them with the help of examples.
Like any modern programming language, Rust too has functions.
The function that you are already familiar with is the main
function. This function gets called when the program is launched.
But what about other functions? In this article, you'll learn to use functions in Rust programs.
The basic syntax of a function
You may already know this based on how we declare the main
function, but let's look at the syntax of declaring a function nonetheless.
// declaring the function
fn function_name() {
<statement(s)>;
}
// calling the function
function_name();
Let's look at a simple function that prints the string "Hi there!" to the standard output.
fn main() {
greet();
}
fn greet() {
println!("Hi there!");
}
And as expected, it has the following output:
Hi there!
That was simple. Let's take it to the next level. Let's create functions that accept parameter(s) and return value(s). Neither are mutually exclusive or inclusive.
Accepting parameters with functions
The syntax for a function that accepts the parameter is as follows:
// declaring the function
fn function_name(variable_name: type) {
<statement(s)>;
}
// calling the function
function_name(value);
You can think of the function parameters as a tuple that is passed to the function. It can accept parameters of multiple data types and as many as you wish. So, you are not restricted to accepting parameters of the same type.
Unlike some languages, Rust does not have default arguments. Populating all parameters when calling the function is compulsory.
Example: Famished function
Let's look at a program to understand this better.
fn main() {
food(2, 4);
}
fn food(theplas: i32, rotis: i32) {
println!(
"I am hungry... I need {} theplas and {} rotis!",
theplas, rotis
);
}
On line 5, I declare a function called food
. This function takes in 2 parameters: theplas
and rotis
(Names of Indian food items). I then print the contents of these variables.
From the main
function, I call the food
function with parameters '2' and '4'. This means that theplas
gets assigned the value '2' and rotis
get assigned the value '4'.
Let's look at the program output:
I am hungry... I need 2 theplas and 4 rotis!
And now I'm actually hungry... π
Returning values from a function
Just as a function can accept values in the form of parameters, a function can also return one or more values. The syntax for such a function is as follows:
// declaring the function
fn function_name() -> data_type {
<statement(s)>;
}
// calling the function
let x = function_name();
The function can return a value using either the return
keyword or by using an expression instead of a statement.
Wait! Expression what?
Before you go further: Statements vs Expressions
It may not fit in the flow of the Rust function examples but you should understand the difference between statements and expressions in Rust and other programming languages.
A statement is a line of code that ends with a semi-colon and does not evaluate to some value. An expression, on the other hand, is a line of code that does not end with a semi-colon and evaluates to some value.
Let's understand that with an example:
fn main() {
let a = 873;
let b = {
// statement
println!("Assigning some value to a...");
// expression
a * 10
};
println!("a: {a}");
}
On line 3, I open a code block, inside which I have a statement and an expression. The comments highlight which one is which.
The code on the 5th line does not evaluate to a value and hence needs to be ended with a semi-colon. This is a statement.
The code on the 8th line evaluates to a value. It is b * 10
which is 873 * 10
and it evaluates to 8730
. Since this line does not end with a semi-colon, this is an expression.
The expressions are not only used to "return" a value from a function. As you just saw, the value of `b * 10` was "returned" from the inner scope to the outer scope and it was assigned to the variable `b`. A simple scope isn't a function and the value of the expression was still "returned".
Example: Buying rusted fruits
Let's understand how a function returns a value using a demonstration.
fn main() {
println!(
"If I buy 2 Kilograms of apples from a fruit vendor, I have to pay {} rupees to them.",
retail_price(2.0)
);
println!(
"But, if I buy 30 Kilograms of apples from a fruit vendor, I have to pay {} rupees to them.",
wholesale_price(30.0)
);
}
fn retail_price(weight: f64) -> f64 {
return weight * 500.0;
}
fn wholesale_price(weight: f64) -> f64 {
weight * 400.0
}
Above I have two functions: retail_price
and wholesale_price
. Both functions accept one parameter and store the value inside the weight
variable. This variable is of type f64
and the function signature denotes that an f64
value is ultimately returned by the function.
Both of these functions multiply the weight of apples purchased by a number. This number represents the current price per Kilogram for apples. Since wholesale purchasers have big orders, logistics are easier in a way, the price can be eased a bit.
Other than the price per Kilogram, the functions have one more difference. That is, the retail_price
function returns the product using the return
keyword. Whereas, the wholesale_price
function returns the product using an expression.
If I buy 2 Kilograms of apples from a fruit vendor, I have to pay 1000 rupees to them.
But, if I buy 30 Kilograms of apples from a fruit vendor, I have to pay 12000 rupees to them.
The output shows that both methods of returning a value from a function work as intended.
Returning multiple values
You can have a function that returns multiple values of different types. You have many options, but returning a tuple is the easiest.
Following is an example:
fn main() {
let (maths, english, science, sanskrit) = tuple_func();
println!("Marks obtained in Maths: {maths}");
println!("Marks obtained in English: {english}");
println!("Marks obtained in Science: {science}");
println!("Marks obtained in Sanskrit: {sanskrit}");
}
fn tuple_func() -> (f64, f64, f64, f64) {
// return marks for a student
let maths = 84.50;
let english = 85.00;
let science = 75.00;
let sanskrit = 67.25;
(maths, english, science, sanskrit)
}
The tuple_func
returns four f64
values, enclosed in a tuple. These values are the marks obtained by a student in four subjects (out of 100).
When the function is called, this tuple is returned. I can either print the values using tuple_name.0
scheme, but I thought it best to destruct the tuple first. That will ease the confusion of which value is which. And I print the marks using the variables that contain values from the destructured tuple.
Following is the output I get:
Marks obtained in Maths: 84.5
Marks obtained in English: 85
Marks obtained in Science: 75
Marks obtained in Sanskrit: 67.25
Conclusion
This article covers functions in the Rust programming language. The "types" of functions are covered here:
- Functions that do not accept any parameter(s) nor return value(s)
- Functions that accept one or more parameters
- Functions that return one or more values back to the caller
You know what comes next? Conditional statements, aka if-else in Rest.
Stay tuned and enjoy learning Rust with It's FOSS.