Functions
cargo run --bin 06_functionsfn names a function. Parameters need types; the return type follows ->. A function with no -> returns (). Functions can be declared in any order.
Rust is expression-oriented. A block { } produces the value of its last expression, and a function body is a block. A semicolon turns an expression into a statement and throws the value away, leaving (). return exits early. let is a statement.
! is the never type, for expressions that never finish: panic!, loop {} with no break, process::exit. Never can stand in for any type.
Parameters, return types, and ()
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn swap(a: String, b: String) -> (String, String) {
(b, a)
}
fn log(msg: &str) {
println!("log: {msg}");
}
fn demo_basics() {
println!("-- basics --");
println!("{}", add(2, 3));
let (left, right) = swap(String::from("left"), String::from("right"));
println!("{left} {right}");
let nothing: () = log("returns unit");
println!("unit {nothing:?}");
}Patterns, mut, and references in parameters
fn head((x, _): (i32, i32)) -> i32 {
x
}
fn bump_copy(mut n: i32) -> i32 {
n += 1;
n
}
fn bump_in_place(n: &mut i32) {
*n += 1;
}
fn total(nums: &[i32]) -> i32 {
nums.iter().sum()
}
fn demo_params() {
println!("-- params --");
println!("head {}", head((9, 1)));
// mut on a parameter changes the local copy only.
let n = 4;
println!("copy {} original {n}", bump_copy(n));
// &mut lets the function change the caller's value.
let mut m = 4;
bump_in_place(&mut m);
println!("in place {m}");
// A slice parameter accepts arrays and Vecs.
println!("totals {} {}", total(&[1, 2, 3]), total(&vec![10, 20]));
}Early return and the tail expression
fn first(nums: &[i32]) -> Option<i32> {
if nums.is_empty() {
return None;
}
Some(nums[0])
}
fn classify(n: i32) -> &'static str {
if n < 0 {
return "negative";
}
if n == 0 { "zero" } else { "positive" }
}
fn demo_returns() {
println!("-- returns --");
println!("{:?} {:?}", first(&[8, 1]), first(&[]));
println!("{} {} {}", classify(-3), classify(0), classify(5));
// fn broken() -> i32 { 5; }
// error[E0308]: mismatched types (the semicolon made the body return ())
}Blocks, if, and match are all expressions
fn demo_expressions() {
println!("-- expressions --");
let n = 3;
let label = if n > 2 { "big" } else { "small" };
let doubled = {
let x = n;
x * 2
};
let word = match n {
1 => "one",
3 => "three",
_ => "many",
};
println!("{label} {doubled} {word}");
// A block that ends with a semicolon has the value ().
let unit = {
let _ = n + 1;
};
println!("unit {unit:?}");
}Scope and drop order
struct Noisy(&'static str);
impl Drop for Noisy {
fn drop(&mut self) {
println!("drop {}", self.0);
}
}
fn demo_scope() {
println!("-- scope --");
let outer = 1;
let _a = Noisy("a");
{
let inner = 2;
let _b = Noisy("b");
println!("inner {inner} outer {outer}");
}
let _c = Noisy("c");
println!("end of demo_scope; c then a drop next, reverse order");
}The never type fits anywhere a value is expected
fn halt(msg: &str) -> ! {
panic!("{msg}");
}
fn pick(flag: bool) -> i32 {
if flag { 7 } else { halt("flag was false") }
}
fn demo_never() {
println!("-- never --");
println!("{}", pick(true));
let input = ["1", "x", "3"];
let mut sum = 0;
for s in input {
// continue has type !, so this match still produces an i32.
let n: i32 = match s.parse() {
Ok(n) => n,
Err(_) => continue,
};
sum += n;
}
println!("sum of valid {sum}");
}Nested functions and function values
fn demo_function_values() {
println!("-- function values --");
fn square(n: i32) -> i32 {
n * n
}
let f: fn(i32, i32) -> i32 = add;
let ops: [(&str, fn(i32) -> i32); 2] = [("square", square), ("bump", bump_copy)];
println!("f(2, 3) = {}", f(2, 3));
for (name, op) in ops {
println!("{name}(5) = {}", op(5));
}
}$ cargo run --bin 06_functions -- basics -- 5 right left log: returns unit unit () -- params -- head 9 copy 5 original 4 in place 5 totals 6 30 -- returns -- Some(8) None negative zero positive -- expressions -- big 6 three unit () -- scope -- inner 2 outer 1 drop b end of demo_scope; c then a drop next, reverse order drop c drop a -- never -- 7 sum of valid 4 -- function values -- f(2, 3) = 5 square(5) = 25 bump(5) = 6
Full program
// 06. Functions, expressions, and blocks
//
// fn names a function. Parameters need types; the return type follows ->. A function
// with no -> returns (). Functions can be declared in any order.
//
// Rust is expression-oriented. A block { } produces the value of its last expression,
// and a function body is a block. A semicolon turns an expression into a statement and
// throws the value away, leaving (). return exits early. let is a statement.
//
// ! is the never type, for expressions that never finish: panic!, loop {} with no
// break, process::exit. Never can stand in for any type.
//
// Run: cargo run --bin 06_functions
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn swap(a: String, b: String) -> (String, String) {
(b, a)
}
fn log(msg: &str) {
println!("log: {msg}");
}
// Example 1: parameters, return types, and ().
fn demo_basics() {
println!("-- basics --");
println!("{}", add(2, 3));
let (left, right) = swap(String::from("left"), String::from("right"));
println!("{left} {right}");
let nothing: () = log("returns unit");
println!("unit {nothing:?}");
}
fn head((x, _): (i32, i32)) -> i32 {
x
}
fn bump_copy(mut n: i32) -> i32 {
n += 1;
n
}
fn bump_in_place(n: &mut i32) {
*n += 1;
}
fn total(nums: &[i32]) -> i32 {
nums.iter().sum()
}
// Example 2: patterns, mut, and references in parameters.
fn demo_params() {
println!("-- params --");
println!("head {}", head((9, 1)));
// mut on a parameter changes the local copy only.
let n = 4;
println!("copy {} original {n}", bump_copy(n));
// &mut lets the function change the caller's value.
let mut m = 4;
bump_in_place(&mut m);
println!("in place {m}");
// A slice parameter accepts arrays and Vecs.
println!("totals {} {}", total(&[1, 2, 3]), total(&vec![10, 20]));
}
fn first(nums: &[i32]) -> Option<i32> {
if nums.is_empty() {
return None;
}
Some(nums[0])
}
fn classify(n: i32) -> &'static str {
if n < 0 {
return "negative";
}
if n == 0 { "zero" } else { "positive" }
}
// Example 3: early return and the tail expression.
fn demo_returns() {
println!("-- returns --");
println!("{:?} {:?}", first(&[8, 1]), first(&[]));
println!("{} {} {}", classify(-3), classify(0), classify(5));
// fn broken() -> i32 { 5; }
// error[E0308]: mismatched types (the semicolon made the body return ())
}
// Example 4: blocks, if, and match are all expressions.
fn demo_expressions() {
println!("-- expressions --");
let n = 3;
let label = if n > 2 { "big" } else { "small" };
let doubled = {
let x = n;
x * 2
};
let word = match n {
1 => "one",
3 => "three",
_ => "many",
};
println!("{label} {doubled} {word}");
// A block that ends with a semicolon has the value ().
let unit = {
let _ = n + 1;
};
println!("unit {unit:?}");
}
// Example 5: scope and drop order.
struct Noisy(&'static str);
impl Drop for Noisy {
fn drop(&mut self) {
println!("drop {}", self.0);
}
}
fn demo_scope() {
println!("-- scope --");
let outer = 1;
let _a = Noisy("a");
{
let inner = 2;
let _b = Noisy("b");
println!("inner {inner} outer {outer}");
}
let _c = Noisy("c");
println!("end of demo_scope; c then a drop next, reverse order");
}
fn halt(msg: &str) -> ! {
panic!("{msg}");
}
fn pick(flag: bool) -> i32 {
if flag { 7 } else { halt("flag was false") }
}
// Example 6: the never type fits anywhere a value is expected.
fn demo_never() {
println!("-- never --");
println!("{}", pick(true));
let input = ["1", "x", "3"];
let mut sum = 0;
for s in input {
// continue has type !, so this match still produces an i32.
let n: i32 = match s.parse() {
Ok(n) => n,
Err(_) => continue,
};
sum += n;
}
println!("sum of valid {sum}");
}
// Example 7: nested functions and function values.
fn demo_function_values() {
println!("-- function values --");
fn square(n: i32) -> i32 {
n * n
}
let f: fn(i32, i32) -> i32 = add;
let ops: [(&str, fn(i32) -> i32); 2] = [("square", square), ("bump", bump_copy)];
println!("f(2, 3) = {}", f(2, 3));
for (name, op) in ops {
println!("{name}(5) = {}", op(5));
}
}
fn main() {
demo_basics();
demo_params();
demo_returns();
demo_expressions();
demo_scope();
demo_never();
demo_function_values();
}