Rust Functions & Closures
Functions are the workhorse of any program, and you already write them every day in TypeScript and JavaScript. This section maps each piece you know — declarations, parameters, return values, arrow functions, higher-order functions, function pointers, and recursion — onto its idiomatic Rust counterpart. The syntax is close enough to feel familiar, but a few deeper ideas (typed signatures as hard contracts, the expression-oriented body, the Fn/FnMut/FnOnce closure traits, and the absence of default/rest parameters and guaranteed tail-call optimization) reshape how you read and write Rust code.
What’s in This Section
Section titled “What’s in This Section”- Basic Functions and Signatures -
fndefinitions, typed parameters, return types, statements vs expressions - Function Parameters - no default or rest parameters; idiomatic alternatives (
Option<T>, slices, structs, traits) - Return Values - return types, tail expressions, the unit type
(), early return, returning tuples - Arrow Functions vs Closures -
|args|syntax,Fn/FnMut/FnOnce, capture by reference vsmove - Higher-Order Functions -
map/filter/reduceequivalents, taking and returning closures - Function Pointers - the
fntype, passing named functions, function items vs closures - Recursion - recursive functions, stack depth, iterative alternatives, recursive enums with
Box
What You’ll Learn
Section titled “What You’ll Learn”By the end of this section, you will be able to:
- Write Rust functions with
fn,snake_casenames, mandatory typed parameters, and a-> Typereturn arrow - Use the tail expression (last line, no semicolon) as the return value, and reserve
returnfor early exits - Reproduce default, optional, and rest parameters with
Option<T>, slices&[T],Defaultstructs, and the builder pattern - Return multiple values as a tuple (or a named struct) instead of mutating out-parameters
- Translate arrow functions into Rust closures and understand capture by
&,&mut, andmove - Distinguish the three closure traits —
Fn,FnMut,FnOnce— and accept the weakest one that works - Take closures as parameters (
impl Fn) and return them (impl FnorBox<dyn Fn>) - Pass named functions and constructors as function pointers (
fn(T) -> R) - Write recursion safely, knowing Rust has no guaranteed tail-call optimization, and reach for iteration or
Boxwhen appropriate
Topics
Section titled “Topics”| # | Topic | What it covers |
|---|---|---|
| 1 | Basic Functions and Signatures | fn definitions, typed parameters, the -> return type, statements vs expressions; vs TS declarations |
| 2 | Function Parameters | No default/rest params; Option<T>, slices &[T], Default structs, builders, “overloading” via traits |
| 3 | Return Values | Return types, implicit tail-expression return, the unit type (), early return, returning tuples |
| 4 | Arrow Functions vs Closures | (args) => becomes |args|; Fn/FnMut/FnOnce; capture by reference vs move; the move keyword |
| 5 | Higher-Order Functions | map/filter/reduce (lazy iterators); taking impl Fn and returning impl Fn / Box<dyn Fn> |
| 6 | Function Pointers | The fn type, passing named functions, function items vs fn pointers vs closures |
| 7 | Recursion | Recursive functions, no guaranteed TCO, stack depth, iterative alternatives, recursive enums with Box |
Learning Objectives
Section titled “Learning Objectives”After completing this section, a TypeScript/JavaScript developer should be able to:
- Read any Rust function signature and explain its parameters and return type as a compiler-enforced contract.
- Explain why a stray semicolon changes a function’s return value to
(), and fix the resultingmismatched typeserror. - Choose the right idiom for “flexible” arguments:
Option<T>, a slice, aDefaultstruct, a builder, or a trait bound. - Write closures with inferred types, decide when
moveis required, and pick the correctFn/FnMut/FnOncebound. - Build lazy iterator pipelines (
iter().filter().map().collect()) as the replacement for eagerArray.prototypechains. - Decide between a
fnpointer and a genericimpl Fnbound for a callback parameter. - Recognize when recursion is safe (shallow, recursive data) versus when iteration is the safer Rust default.
Prerequisites
Section titled “Prerequisites”This section assumes you have completed:
- Section 02: Basics — variables and mutability (
let mutis essential forFnMut), the basic types you will pass as parameters, and especially the statement-vs-expression distinction that underpins tail-expression returns.
If the expression-oriented model (let x = { ...; a + b };) feels unfamiliar, re-read the Basics section before starting here.
Note: Several topics in this section preview concepts covered fully later: ownership and
move(Section 05),Option/Resultand the?operator (Section 08), generics and traits (Section 09), andBox/Rcfor recursive types (Section 10). You do not need those sections first — the links are there for when you want to go deeper.
Estimated Time
Section titled “Estimated Time”- Reading: 3-4 hours
- Hands-on Practice & Exercises: 3-4 hours
- Total: 6-8 hours
A reasonable order is the list order above: basics → parameters → return values → closures → higher-order → function pointers → recursion. Closures (topic 4) are the conceptual heart of the section, so do not skip them.
Frequently asked questions
Section titled “Frequently asked questions”How do I write a default or optional parameter?
Section titled “How do I write a default or optional parameter?”Rust has neither. Model an optional argument as Option<T> and pass None, or use the builder pattern when there are many optionals. For overloading-like flexibility, take a generic parameter bounded by a trait. See Function Parameters.
What is the difference between a function and a closure?
Section titled “What is the difference between a function and a closure?”A fn is a plain named function. A closure (|x| x + 1) can capture variables from its surroundings, and the compiler sorts closures into the Fn, FnMut, and FnOnce traits by how they use what they capture. See Arrow Functions vs Closures.
Why doesn’t my function need a return?
Section titled “Why doesn’t my function need a return?”The last expression in a block is its value, so fn add(a: i32, b: i32) -> i32 { a + b } returns a + b with no return and no trailing semicolon. An explicit return is reserved for early exits. See Return Values.