286 lines
14 KiB
HTML
286 lines
14 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Functions</title>
|
||
</head>
|
||
<body>
|
||
<h2 id="functions"><a class="header" href="#functions">Functions</a></h2>
|
||
<p>Functions are prevalent in Rust code. You’ve already seen one of the most
|
||
important functions in the language: the <code>main</code> function, which is the entry
|
||
point of many programs. You’ve also seen the <code>fn</code> keyword, which allows you to
|
||
declare new functions.</p>
|
||
<p>Rust code uses <em>snake case</em> as the conventional style for function and variable
|
||
names, in which all letters are lowercase and underscores separate words.
|
||
Here’s a program that contains an example function definition:</p>
|
||
<p><span class="filename">Filename: src/main.rs</span></p>
|
||
<pre class="playground"><code class="language-rust edition2024">fn main() {
|
||
println!("Hello, world!");
|
||
|
||
another_function();
|
||
}
|
||
|
||
fn another_function() {
|
||
println!("Another function.");
|
||
}</code></pre>
|
||
<p>We define a function in Rust by entering <code>fn</code> followed by a function name and a
|
||
set of parentheses. The curly brackets tell the compiler where the function
|
||
body begins and ends.</p>
|
||
<p>We can call any function we’ve defined by entering its name followed by a set
|
||
of parentheses. Because <code>another_function</code> is defined in the program, it can be
|
||
called from inside the <code>main</code> function. Note that we defined <code>another_function</code>
|
||
<em>after</em> the <code>main</code> function in the source code; we could have defined it before
|
||
as well. Rust doesn’t care where you define your functions, only that they’re
|
||
defined somewhere in a scope that can be seen by the caller.</p>
|
||
<p>Let’s start a new binary project named <em>functions</em> to explore functions
|
||
further. Place the <code>another_function</code> example in <em>src/main.rs</em> and run it. You
|
||
should see the following output:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling functions v0.1.0 (file:///projects/functions)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28s
|
||
Running `target/debug/functions`
|
||
Hello, world!
|
||
Another function.
|
||
</code></pre>
|
||
<p>The lines execute in the order in which they appear in the <code>main</code> function.
|
||
First the “Hello, world!” message prints, and then <code>another_function</code> is called
|
||
and its message is printed.</p>
|
||
<h3 id="parameters"><a class="header" href="#parameters">Parameters</a></h3>
|
||
<p>We can define functions to have <em>parameters</em>, which are special variables that
|
||
are part of a function’s signature. When a function has parameters, you can
|
||
provide it with concrete values for those parameters. Technically, the concrete
|
||
values are called <em>arguments</em>, but in casual conversation, people tend to use
|
||
the words <em>parameter</em> and <em>argument</em> interchangeably for either the variables
|
||
in a function’s definition or the concrete values passed in when you call a
|
||
function.</p>
|
||
<p>In this version of <code>another_function</code> we add a parameter:</p>
|
||
<p><span class="filename">Filename: src/main.rs</span></p>
|
||
<pre class="playground"><code class="language-rust edition2024">fn main() {
|
||
another_function(5);
|
||
}
|
||
|
||
fn another_function(x: i32) {
|
||
println!("The value of x is: {x}");
|
||
}</code></pre>
|
||
<p>Try running this program; you should get the following output:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling functions v0.1.0 (file:///projects/functions)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.21s
|
||
Running `target/debug/functions`
|
||
The value of x is: 5
|
||
</code></pre>
|
||
<p>The declaration of <code>another_function</code> has one parameter named <code>x</code>. The type of
|
||
<code>x</code> is specified as <code>i32</code>. When we pass <code>5</code> in to <code>another_function</code>, the
|
||
<code>println!</code> macro puts <code>5</code> where the pair of curly brackets containing <code>x</code> was
|
||
in the format string.</p>
|
||
<p>In function signatures, you <em>must</em> declare the type of each parameter. This is
|
||
a deliberate decision in Rust’s design: Requiring type annotations in function
|
||
definitions means the compiler almost never needs you to use them elsewhere in
|
||
the code to figure out what type you mean. The compiler is also able to give
|
||
more-helpful error messages if it knows what types the function expects.</p>
|
||
<p>When defining multiple parameters, separate the parameter declarations with
|
||
commas, like this:</p>
|
||
<p><span class="filename">Filename: src/main.rs</span></p>
|
||
<pre class="playground"><code class="language-rust edition2024">fn main() {
|
||
print_labeled_measurement(5, 'h');
|
||
}
|
||
|
||
fn print_labeled_measurement(value: i32, unit_label: char) {
|
||
println!("The measurement is: {value}{unit_label}");
|
||
}</code></pre>
|
||
<p>This example creates a function named <code>print_labeled_measurement</code> with two
|
||
parameters. The first parameter is named <code>value</code> and is an <code>i32</code>. The second is
|
||
named <code>unit_label</code> and is type <code>char</code>. The function then prints text containing
|
||
both the <code>value</code> and the <code>unit_label</code>.</p>
|
||
<p>Let’s try running this code. Replace the program currently in your <em>functions</em>
|
||
project’s <em>src/main.rs</em> file with the preceding example and run it using <code>cargo run</code>:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling functions v0.1.0 (file:///projects/functions)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
|
||
Running `target/debug/functions`
|
||
The measurement is: 5h
|
||
</code></pre>
|
||
<p>Because we called the function with <code>5</code> as the value for <code>value</code> and <code>'h'</code> as
|
||
the value for <code>unit_label</code>, the program output contains those values.</p>
|
||
<h3 id="statements-and-expressions"><a class="header" href="#statements-and-expressions">Statements and Expressions</a></h3>
|
||
<p>Function bodies are made up of a series of statements optionally ending in an
|
||
expression. So far, the functions we’ve covered haven’t included an ending
|
||
expression, but you have seen an expression as part of a statement. Because
|
||
Rust is an expression-based language, this is an important distinction to
|
||
understand. Other languages don’t have the same distinctions, so let’s look at
|
||
what statements and expressions are and how their differences affect the bodies
|
||
of functions.</p>
|
||
<ul>
|
||
<li><em>Statements</em> are instructions that perform some action and do not return
|
||
a value.</li>
|
||
<li><em>Expressions</em> evaluate to a resultant value.</li>
|
||
</ul>
|
||
<p>Let’s look at some examples.</p>
|
||
<p>We’ve actually already used statements and expressions. Creating a variable and
|
||
assigning a value to it with the <code>let</code> keyword is a statement. In Listing 3-1,
|
||
<code>let y = 6;</code> is a statement.</p>
|
||
<figure class="listing" id="listing-3-1">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">fn main() {
|
||
let y = 6;
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-3-1">Listing 3-1</a>: A <code>main</code> function declaration containing one statement</figcaption>
|
||
</figure>
|
||
<p>Function definitions are also statements; the entire preceding example is a
|
||
statement in itself. (As we’ll see shortly, calling a function is not a
|
||
statement, though.)</p>
|
||
<p>Statements do not return values. Therefore, you can’t assign a <code>let</code> statement
|
||
to another variable, as the following code tries to do; you’ll get an error:</p>
|
||
<p><span class="filename">Filename: src/main.rs</span></p>
|
||
<pre><code class="language-rust ignore does_not_compile">fn main() {
|
||
let x = (let y = 6);
|
||
}</code></pre>
|
||
<p>When you run this program, the error you’ll get looks like this:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling functions v0.1.0 (file:///projects/functions)
|
||
error: expected expression, found `let` statement
|
||
--> src/main.rs:2:14
|
||
|
|
||
2 | let x = (let y = 6);
|
||
| ^^^
|
||
|
|
||
= note: only supported directly in conditions of `if` and `while` expressions
|
||
|
||
warning: unnecessary parentheses around assigned value
|
||
--> src/main.rs:2:13
|
||
|
|
||
2 | let x = (let y = 6);
|
||
| ^ ^
|
||
|
|
||
= note: `#[warn(unused_parens)]` on by default
|
||
help: remove these parentheses
|
||
|
|
||
2 - let x = (let y = 6);
|
||
2 + let x = let y = 6;
|
||
|
|
||
|
||
warning: `functions` (bin "functions") generated 1 warning
|
||
error: could not compile `functions` (bin "functions") due to 1 previous error; 1 warning emitted
|
||
</code></pre>
|
||
<p>The <code>let y = 6</code> statement does not return a value, so there isn’t anything for
|
||
<code>x</code> to bind to. This is different from what happens in other languages, such as
|
||
C and Ruby, where the assignment returns the value of the assignment. In those
|
||
languages, you can write <code>x = y = 6</code> and have both <code>x</code> and <code>y</code> have the value
|
||
<code>6</code>; that is not the case in Rust.</p>
|
||
<p>Expressions evaluate to a value and make up most of the rest of the code that
|
||
you’ll write in Rust. Consider a math operation, such as <code>5 + 6</code>, which is an
|
||
expression that evaluates to the value <code>11</code>. Expressions can be part of
|
||
statements: In Listing 3-1, the <code>6</code> in the statement <code>let y = 6;</code> is an
|
||
expression that evaluates to the value <code>6</code>. Calling a function is an
|
||
expression. Calling a macro is an expression. A new scope block created with
|
||
curly brackets is an expression, for example:</p>
|
||
<p><span class="filename">Filename: src/main.rs</span></p>
|
||
<pre class="playground"><code class="language-rust edition2024">fn main() {
|
||
let y = {
|
||
let x = 3;
|
||
x + 1
|
||
};
|
||
|
||
println!("The value of y is: {y}");
|
||
}</code></pre>
|
||
<p>This expression:</p>
|
||
<pre><code class="language-rust ignore">{
|
||
let x = 3;
|
||
x + 1
|
||
}</code></pre>
|
||
<p>is a block that, in this case, evaluates to <code>4</code>. That value gets bound to <code>y</code>
|
||
as part of the <code>let</code> statement. Note the <code>x + 1</code> line without a semicolon at
|
||
the end, which is unlike most of the lines you’ve seen so far. Expressions do
|
||
not include ending semicolons. If you add a semicolon to the end of an
|
||
expression, you turn it into a statement, and it will then not return a value.
|
||
Keep this in mind as you explore function return values and expressions next.</p>
|
||
<h3 id="functions-with-return-values"><a class="header" href="#functions-with-return-values">Functions with Return Values</a></h3>
|
||
<p>Functions can return values to the code that calls them. We don’t name return
|
||
values, but we must declare their type after an arrow (<code>-></code>). In Rust, the
|
||
return value of the function is synonymous with the value of the final
|
||
expression in the block of the body of a function. You can return early from a
|
||
function by using the <code>return</code> keyword and specifying a value, but most
|
||
functions return the last expression implicitly. Here’s an example of a
|
||
function that returns a value:</p>
|
||
<p><span class="filename">Filename: src/main.rs</span></p>
|
||
<pre class="playground"><code class="language-rust edition2024">fn five() -> i32 {
|
||
5
|
||
}
|
||
|
||
fn main() {
|
||
let x = five();
|
||
|
||
println!("The value of x is: {x}");
|
||
}</code></pre>
|
||
<p>There are no function calls, macros, or even <code>let</code> statements in the <code>five</code>
|
||
function—just the number <code>5</code> by itself. That’s a perfectly valid function in
|
||
Rust. Note that the function’s return type is specified too, as <code>-> i32</code>. Try
|
||
running this code; the output should look like this:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling functions v0.1.0 (file:///projects/functions)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.30s
|
||
Running `target/debug/functions`
|
||
The value of x is: 5
|
||
</code></pre>
|
||
<p>The <code>5</code> in <code>five</code> is the function’s return value, which is why the return type
|
||
is <code>i32</code>. Let’s examine this in more detail. There are two important bits:
|
||
First, the line <code>let x = five();</code> shows that we’re using the return value of a
|
||
function to initialize a variable. Because the function <code>five</code> returns a <code>5</code>,
|
||
that line is the same as the following:</p>
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||
</span><span class="boring">fn main() {
|
||
</span>let x = 5;
|
||
<span class="boring">}</span></code></pre>
|
||
<p>Second, the <code>five</code> function has no parameters and defines the type of the
|
||
return value, but the body of the function is a lonely <code>5</code> with no semicolon
|
||
because it’s an expression whose value we want to return.</p>
|
||
<p>Let’s look at another example:</p>
|
||
<p><span class="filename">Filename: src/main.rs</span></p>
|
||
<pre class="playground"><code class="language-rust edition2024">fn main() {
|
||
let x = plus_one(5);
|
||
|
||
println!("The value of x is: {x}");
|
||
}
|
||
|
||
fn plus_one(x: i32) -> i32 {
|
||
x + 1
|
||
}</code></pre>
|
||
<p>Running this code will print <code>The value of x is: 6</code>. But what happens if we
|
||
place a semicolon at the end of the line containing <code>x + 1</code>, changing it from
|
||
an expression to a statement?</p>
|
||
<p><span class="filename">Filename: src/main.rs</span></p>
|
||
<pre><code class="language-rust ignore does_not_compile">fn main() {
|
||
let x = plus_one(5);
|
||
|
||
println!("The value of x is: {x}");
|
||
}
|
||
|
||
fn plus_one(x: i32) -> i32 {
|
||
x + 1;
|
||
}</code></pre>
|
||
<p>Compiling this code will produce an error, as follows:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling functions v0.1.0 (file:///projects/functions)
|
||
error[E0308]: mismatched types
|
||
--> src/main.rs:7:24
|
||
|
|
||
7 | fn plus_one(x: i32) -> i32 {
|
||
| -------- ^^^ expected `i32`, found `()`
|
||
| |
|
||
| implicitly returns `()` as its body has no tail or `return` expression
|
||
8 | x + 1;
|
||
| - help: remove this semicolon to return this value
|
||
|
||
For more information about this error, try `rustc --explain E0308`.
|
||
error: could not compile `functions` (bin "functions") due to 1 previous error
|
||
</code></pre>
|
||
<p>The main error message, <code>mismatched types</code>, reveals the core issue with this
|
||
code. The definition of the function <code>plus_one</code> says that it will return an
|
||
<code>i32</code>, but statements don’t evaluate to a value, which is expressed by <code>()</code>,
|
||
the unit type. Therefore, nothing is returned, which contradicts the function
|
||
definition and results in an error. In this output, Rust provides a message to
|
||
possibly help rectify this issue: It suggests removing the semicolon, which
|
||
would fix the error.</p>
|
||
</body>
|
||
</html>
|