Beau
Okay, Jo. So, we've gone through variables, loops, even that... intense ownership stuff. And I'm getting it. But my 'main' function is starting to look like a junk drawer. Everything is just... in there.
Transcript
Beau
Okay, Jo. So, we've gone through variables, loops, even that... intense ownership stuff. And I'm getting it. But my 'main' function is starting to look like a junk drawer. Everything is just... in there.
Jo
That is the perfect way to describe the next step. Your junk drawer is about to get some organizers. We've been putting all our logic in one place, but that's not how real programs are built. We need to talk about functions.
Beau
Right, functions. I've seen them in other languages. They're like... little self-contained code-chunks you can call whenever you need them?
Jo
Exactly. You've already been using one this whole time: 'main'. But you can make your own. The keyword in Rust is 'fn', just 'f' 'n'. So you'd write 'fn do_something()' and then put your code in curly braces.
Beau
Okay, so instead of my loop for printing numbers being inside 'main', I could pull it out into its own function called, I don't know, 'fn print_numbers()'?
Jo
You absolutely could. And then inside 'main', all you have to write is 'print_numbers();'. Suddenly your 'main' function is readable. It just says what it's doing, not how it's doing it.
Beau
I like that. A lot. But what if... what if my 'print_numbers' function needs to be more flexible? Like, one time I want to print up to 10, another time up to 50. How do I tell it that?
Jo
That's the next piece: parameters. They're the bits of information you pass *into* a function. You declare them inside the parentheses. So you'd change your function definition to 'fn print_numbers(max_number: i32)'.
Beau
Ah, okay, so 'max_number' is the name I'll use inside the function, and 'i32' is me telling Rust, 'hey, expect a 32-bit integer here'. You always have to declare the type, right?
Jo
Always. Rust is very strict about that. No ambiguity. Then, when you call it from 'main', you'd write 'print_numbers(10);' or 'print_numbers(50);'. You're passing the value in.
Beau
Got it. Pass data in. But what about getting data *out*? What if I want a function that, say, adds two numbers together and gives me the result back so I can use it for something else?
Jo
That's a return value. And Rust has a... particular, but very cool, way of handling this. You declare the return type after the parentheses using a little arrow, a dash and a greater-than sign. So, 'fn add_two_numbers(x: i32, y: i32) -> i32'.
Beau
So that arrow and 'i32' is a promise. This function *will* give you back an integer.
Jo
Exactly. Now for the cool part. You *can* use the 'return' keyword, like in other languages. But the idiomatic Rust way is to make the last line of the function the value you want to return, and—this is key—you leave off the semicolon.
Beau
Wait, what? No semicolon? Why?
Jo
Because in Rust, a line with a semicolon is a 'statement'. It does something, but it doesn't have a value. A line *without* a semicolon is an 'expression'—it evaluates to a value. So in our 'add_two_numbers' function, the entire body can just be 'x + y'. No semicolon.
Beau
That's... weirdly elegant. So in 'main' I'd write something like 'let sum = add_two_numbers(5, 7);' and the variable 'sum' would just... get the value 12?
Jo
Precisely. The expression 'x + y' evaluates to 12, and since it's the last line, that becomes the function's return value, which gets assigned to 'sum'.
Beau
Okay, that's clicking. My junk drawer is now neatly organized into little labeled boxes called functions. But... now I just have a file with a hundred little boxes. What if I have a really big project? A file with hundreds of functions still feels messy.
Jo
And that is the perfect segue to the next level of organization: modules. If functions are the labeled boxes, modules are the shelves and cabinets you put those boxes in. They're for grouping related functions together.
Beau
So I could have a module for, like, all my math functions, and another one for all my text-printing functions?
Jo
Exactly. You use the keyword 'mod'. So you'd write 'mod math { ... }' and put your 'add_two_numbers' function inside those curly braces. Then you'd have 'mod text_utils { ... }' for your printing stuff.
Beau
How do I... how do I get to them from 'main' then? If 'add_two_numbers' is now tucked away inside a 'math' module?
Jo
You use what's called a path, with two colons. So from 'main', you'd call 'math::add_two_numbers(5, 7);'. It's like telling Rust, 'go into the math cabinet and find the box labeled add_two_numbers'.
Beau
But wait, wouldn't that just... not work? I feel like there's a catch.
Jo
There is. You're thinking like a Rust developer already. By default, everything inside a module is private. It's hidden. If you want a function to be callable from outside its module, you have to explicitly make it public.
Beau
How? A keyword?
Jo
Yep. The keyword 'pub', for public. So inside your math module, the function definition becomes 'pub fn add_two_numbers...'. You're telling Rust, 'this specific function is part of my public API. Anyone outside this module is allowed to use it.'
Beau
So I can have helper functions inside the module that are private, that only 'add_two_numbers' uses, and then just expose the one useful function to the rest of my code. That's actually really smart. It prevents me from accidentally using the wrong internal bits.
Jo
That's the entire design philosophy. Encapsulation. Only expose what needs to be exposed. It makes your code safer and much easier to change later, because you know nobody is depending on your private helper functions.
Beau
Okay. So we went from a messy junk drawer in 'main', to organized boxes with functions, and now to shelves and cabinets with modules. It feels like building with proper, labeled blocks instead of just a pile of clay.
Jo
It's the foundation of any non-trivial program. You group related behavior into functions, and you group related functions into modules. Get that right, and you can build almost anything without it collapsing under its own weight.