Rust functions are private by default, and are defined with the keyword fn:
fn main() {
println!("Hello, world!")
}
The keyword pub must be used before fn in order to make a function public, and therefore accessible in other Rust programs.
Every Rust program needs a main function, which contains the only part of the program that will be executed. All other separately defined functions need to be included, or called, inside this function:
fn main() {
println!("This is a string")
}
fn hidden_string() {
println!("Ooh, secret string!") // Compiles, does not execute
}
Rust functions can implicitly return the last found value in the function body as an expression, as long as the return value is explicitly typed:
fn main() {
println!("{}", character())
}
fn character() {
'f' // Return value of function without semicolon, recognised as expression
}
error[E0277]: `()` doesn't implement `std::fmt::Display`
--> src/main.rs:2:20
|
2 | println!("{}", character())
| `()` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `()`
error[E0308]: mismatched types
--> src/main.rs:6:5
|
5 | fn character() {
| - help: try adding a return type: `-> char`
6 | 'f' // Return value of function without semicolon
| ^^^ expected `()`, found `char`
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
Because the return value wasn't explicitly given for the character() function, and because it's trying to return an expression, the function returned the empty unit type, defined as ().
In Rust, the results of functions can immediately be assigned to variables:
fn main() {
let x: i32 = {
let mut y: i32 = 0;
while y < 5 {
y += 1;
}
y
};
println!("{}", x)
}
Functions can be written to take parameters, variables defined in the brackets next to the function name that can then be used in the body of a function:
fn main() {
println!("{:?}", reverse_nums(9, 10))
}
fn reverse_nums(i: i32, j: i32) -> (i32, i32) {
(j, i)
}
Function parameters always need explicit type definitions.
Note the syntax between the curly brackets in the string formatting of the println!() call in the above example. This prints the result of the supplied function call in a debug view, allowing for the printing of data structures like structs, enums and tuples (explained later).
Exercise:
Add a function below named num that takes a u32 and returns it unchanged, without using the return keyword:
Functions can also be implemented on structs and enums, as methods, with the impl keyword:
struct Train {
colour: String,
num_of_coaches: i32,
max_speed_kmh: f64
}
impl Train {
pub fn how_many_coaches(&self) {
println!("{}", &self.num_of_coaches);
}
}
fn main() {
let t = Train {
colour: String::from("Green"),
num_of_coaches: 5,
max_speed_kmh: 120.05
};
t.how_many_coaches();
}
The syntax involving the ampersand symbol (&) in the above example will be fully explained in the next chapter. For now, note that in order for the Train struct t to access its own fields, it has to get them using the self keyword. This allows structs and enums to refer to themselves in their methods, and perform actions like calling other functions and methods on themselves.
As mentioned before, println!() is not a function, but a macro.
Function-like macros, explained very briefly, are part of a larger, much more complicated collection of functionality in the Rust language.
They appear to be normal functions, except for the exclamation point used in their names:
The above examples of Rust macros are all built into the standard library.
The main feature that sets function-like macros apart from functions is that macros can take variable numbers of arguments as input:
fn main() {
println!("Hello, world!");
// One argument given to println!()
let x = 5;
println!("The value of x is {}", x);
// Two arguments given:
// Formatted string and one variable to print with string
let y = vec![10, 100, 1000];
// Creating a vector (mutable Rust array) with vec!
println!("The values of vector y are {}, {} and {}", y[0], y[1], y[2]);
// Four arguments given:
// Formatted string with three inputs, and three vector elements
}