Functions
Functions are one of the most fundamental concepts in Rust.
You define a function by using the fn
keyword.
Input Arguments
Rust functions can define zero or more input arguments. Each input argument must declare its data type.
For example, check out the input arguments declared within the parentheses of the following function.
#![allow(unused)] fn main() { fn get_full_name(first_name: String, last_name: String) { return format!("{} {}", first_name, last_name); } }
Return Values
Functions can optionally define a return value.
By default, the return value of a function is the Rust "unit" type, displayed as a pair of empty parentheses ()
.
#![allow(unused)] fn main() { fn get_name() { } // This function returns a unit type }
To specify an alternate return value than the "unit" type, add a "thin arrow" after your function input arguments, and specify the return type.
After specifying the return type, you can use the return
keyword to specify a value that you want the function to return, inside the function body.
#![allow(unused)] fn main() { fn get_name() -> String { return "Trevor".to_string(); } }
In addition to returning primitive types, such as integers, floating-point values, and strings, you can also return enum
variants or an instance of a user-defined struct
.
In the following example, we'll define a Dog
struct with a name
field, and then return a new instance of the Dog
type to the function caller.
Note that the function named get_dog
has declared a return type of Dog
.
#![allow(unused)] fn main() { struct Dog { name: String, } fn get_dog() -> Dog { return Dog{ name: "Fido".to_string() }; } }
Calling Functions
To call a Rust function from your application or library, use the function name, followed by parentheses, containing the input arguments.
Rust does NOT support named arguments, as some other languages do. All input arguments are interpreted positionally, according to the order they're declared in. The documentation for a given function, or your development environment, should indicate which order the function arguments ought to be passed in.
Let's define a function with input arguments and a return type.
#![allow(unused)] fn main() { fn get_name(first: &str, last: &str) -> String { let mut new_string = String::from(first); new_string.push_str(" "); new_string.push_str(last); return new_string; } }
To call this function, let's try the following example:
fn main() { let full_name = get_name("Trevor", "Sullivan"); println!("{full_name}"); }
Discard Function Return Value
Capturing the return value of a function call is optional. To discard the return value of a function, you can either:
- Skip assigning the result to a variable
- Assign the function's result to the underscore character
_
fn main() { _ = get_name("Trevor", "Sullivan"); // Valid, explicitly discards the function return value get_name("Trevor", "Sullivan"); // Valid, implicitly discards the function return value }
In some cases, the Rust compiler will issue a warning for implicitly discarding a function return value.
For example, if a function returns a Result<T, E>
, you might see the following warning:
unused
Result
that must be used thisResult
may be anErr
variant, which should be handled#[warn(unused_must_use)]
on by default
Here's an example:
fn get_name(first: &str, last: &str) -> Result<String, Box<dyn std::error::Error>> { let mut new_string = String::from(first); new_string.push_str(" "); new_string.push_str(last); return Ok(new_string); } fn main() { get_name("Trevor", "Sullivan"); }
Async Functions
Rust functions can be declared as asynchronous, which means that they will return a type implementing the Future
trait, by default.
You can use the async
Rust keyword to denote a function as being asynchronous.
Keep in mind that a Future
will not be executed unless you include an async executor in your application.
You can write your own executor or you can use an off-the-shelf async executor like smol
or tokio
.
#![allow(unused)] fn main() { async fn get_age() -> i32 { return 34; } }
We will discuss asynchronous functions, in more depth, in a separate section.