2.5Why functions are useful

Last updated June 11, 2026

Now that you can write functions, an honest question deserves an honest answer: why bother? Everything so far could have been one long main. For a ten-line program, it even should be. But programs grow, and somewhere around the point where one screen can't hold main, functions change from ceremony into survival gear. Here's precisely what they buy.

Organization. A 5,000-line main is a junk drawer; nobody finds anything, and every change risks disturbing strangers. The same code as a hundred fifty-line functions is a labeled toolbox. Each function has a name, a job, and a boundary, and the boundary is real: as lesson 2.4 showed, what happens in a function stays in a function, except what's deliberately passed through the doors.

Reuse. Code in a function runs anywhere, any number of times, by name. That's the DRY principle from lesson 2.2 made practical, and it's bigger than saving keystrokes: when duplicated logic needs a fix, every copy is a chance to forget one. Code that exists once gets fixed once.

Testing. A function with defined inputs and outputs can be checked by itself: call it with known arguments, compare against known answers. You can do that by hand today (a few temporary calls in main), and chapter 14 will show you Rust's built-in machinery that does it automatically, forever, on every build. That machinery needs the seams functions create; you can't unit-test a paragraph in the middle of a 500-line main.

Extensibility. When behavior lives in one function, upgrading the behavior is editing one place, and every caller benefits without knowing anything changed.

Abstraction. The quiet superpower. To use a function, you need its name, inputs, and output. You don't need to know how it works inside. You've been living this all course: you have no idea how println! arranges for characters to appear on your screen, and it has cost you nothing. Every function you write extends that same courtesy to its callers, including future-you, who will gratefully read let total = score_for_round(rolls); without re-deriving the scoring rules.

When to reach for one

Useful rules of thumb, tuned for where you are now:

Code that appears more than once wants to be a function (that's DRY again, and it's the most mechanical trigger). A chunk with one clear job and an obvious input/output shape wants to be a function even if it appears once, because the name documents the job. And when a function has grown past "fits comfortably on a screen" or needs the word "and" twice to describe, it wants to become two.

The most common shape for small interactive programs gives you the pattern for free: something like read the inputs, compute the result, print the result. That's three jobs; lesson 2.6 makes a method of it.

One anti-pattern to dodge from day one: the function that computes a result and also prints it. Two jobs in one body means no caller can ever get the value without the printing (or the printing without the value), and the chapter 14 test machinery can't check it cleanly. Compute or print; let main introduce them to each other.

Best practice

One function, one job, named for the job. If naming it requires "and," it's two functions wearing a coat.

Quiz time

Question #1

This function bundles two jobs. What are they, and what should the split look like?

fn report_area(width: i32, height: i32) {
    let area = width * height;
    println!("The area is {area}");
}
Show solution

It computes the area and prints it. Split: fn area(width: i32, height: i32) -> i32 { width * height }, and let the caller decide what to do with the value (print it, compare it, store it). The compute half is now reusable and testable; printing stays a one-liner in main.

Question #2

Your program asks for three numbers using three copies of the read-and-parse recipe. What's the refactor, and which benefit from this lesson is doing the work?

Show solution

Extract one get_number() -> i32 function and call it three times (exactly as lesson 2.2 did). Primarily reuse/DRY, with testing and extensibility riding along: when input handling improves in chapter 12, one function upgrades all three call sites.

Functions are worth using well, which raises the design question: given a problem, how do you decide what the functions are? That's the next lesson, and it's the chapter's payoff.