Functions & Comments in Rust
With the knowledge of variables and data types, it is time to put them to use. Functions in Rust
should be familiar. The main
function is one, for example, that you have already seen several
times in the Rust Playground.
// This is a function!
fn main() {}
Functions contain pieces of logic in a context-specific scope. They are useful for splitting your program into more digestible pieces rather than just using a single main function to operate.
Declaring a function is simple - start with fn
, followed by the name and two empty parentheses,
and closed with curly brackets:
fn main() {
// The main function is no longer alone here.
do_something_interesting();
}
fn do_something_interesting() {
println!("Something interesting!");
}
You may have noticed that we called the do_something_interesting()
function within our main
function. It doesn't matter where this function is declared or what it does. It will execute as long
as it's valid, safe Rust code.
Parameters
Functions in this form aren't useful. The above is pointless; we could accomplish the same result with less code.
Parameters allow for functions to have more dynamic and custom input. For example, let's take the previous example and add a parameter:
fn main() {
// The main function is no longer alone here.
do_something_interesting(true);
}
fn do_something_interesting(is_interesting: bool) {
println!("Is this interesting: {is_interesting}.");
}
// Output: Is this interesting: true.
A parameter is added within the previously empty parentheses. It takes the name, is_interesting
followed by a colon (:
), and the type, bool
. This tells the function that it also expects a
boolean to be included as a parameter when it is called. These parameters become part of the
function's signature, or the unique layout of the function.
You can include multiple parameters of multiple types:
fn main() {
// The main function is no longer alone here.
do_something_interesting(true, "Bader");
}
fn do_something_interesting(is_interesting: bool, name: &str) {
println!("Hey, {name}! Is this interesting: {is_interesting}.");
}
// Output: Hey, Bader! Is this interesting: true.
Statements and Expressions
It's essential to differentiate statements versus expressions in Rust. Functions in Rust are statements that can end in an expression. The difference is:
- Statements perform some modification and do not return any value.
- Expressions provide a conclusion in the form of a value.
A simple way to think about this is when you declare something purely definitive in Rust, whether a variable or a function, it is a statement.
// This is a statement - it states that x is `10` and does not return anything.
let x = 10;
For expressions, they must evaluate and express a final value. Take this example:
// Defining a new scope within main.
let y = {
let x = 3;
x + 1 // An expression that returns 4
};
Notice the lack of a semicolon at the end of x + 1
. As soon as you add a semicolon to a line's
end, it becomes a statement. Expressions do not have semicolons appended to the end of them.
Return types
A key part of functions in Rust is the ability to specify a return type. With the knowledge that we can use expressions to have a conclusion to a series of operations, we can use the following syntax to add a return type and result to our function:
// This function takes a number and returns the squared version of it.
fn square(x: i32) -> i32 {
x * x
}
Notice the return type is denoted by the arrow (->
) followed by the type we wish to return. To
return the type, we simply return the expression without a semicolon. It's possible to also
explicitly define a return statement:
// This function takes a number and returns the squared version of it.
fn square(x: i32) -> i32 {
// This is also valid!
return x * x;
}
Comments
Comments are used to document parts of your code in order to provide clarification when needed. Not
every line needs to be commented on. However, it may be helpful to provide context in certain
situations. Double forward slashes usually precede them (//
).
Comments are purely for developers and not realized in the compiler.
You have already seen comments in action within the examples in this course. Take the previous example:
fn main() {
// The main function is no longer alone here.
do_something_interesting(true, "Bader");
}
// Hey, I'm a comment! I explain things
// This function does something interesting, apparently.
fn do_something_interesting(is_interesting: bool, name: &str) {
println!("Hey, {name}! Is this interesting: {is_interesting}.");
}
// Output: Hey, Bader! Is this interesting: true.
In the last module, you'll learn how to properly document your code using comments in a way that Cargo can understand.
Try it out!
What is happening here?
A new function with zero parameters is introduced, called do_something_better
. You should be able
to add a new parameter of type f64
(for double precision) like so:
fn do_something_better(number: f64) {
println("{number}");
}
To expand it, let's specify a return type - in this case, we want to return the number we passed in but squared:
fn do_something_better(number: f64) -> f64 {
number * number
}
Notice the lack of a semicolon, which denotes a resultant value in the form of an expression.