The problem with repeated code
Suppose a teacher asks three students to write an apology, and each apology is three lines long. Written directly, that is nine println! calls, and the only difference between the groups of three is the name:
println!("This is Andy");
println!("Andy made a mistake");
println!("Andy suffers the consequences");
println!("This is Mike");
println!("Mike made a mistake");
println!("Mike suffers the consequences");
This works, but it does not scale. Changing the wording means editing every group. Going from 3 students to 30 means copying and editing 27 more lines, and every copy is a place to make a typo.
One function with a parameter
The part that changes becomes a parameter, and the part that stays the same goes in the function body. You write the apology once and call it once per student:
fn print_message(name: &str) {
println!("This is {}", name);
println!("{} made a mistake", name);
println!("{} suffers the consequences", name);
}
fn main() {
print_message("Andy");
print_message("Mike");
print_message("Sally");
}
Now each change has one home:
- To change who apologizes, change the arguments in the calls.
- To change what the apology says, change the body of the function. Every call picks up the new text.
Note that the same parameter name appears three times in the body. A parameter can be used as often as needed, and each use sees the same value for that call.
More than one parameter
When two things vary, use two parameters. Arguments are matched to parameters by position, and each parameter has its own type. In the exercise, the function needs both a name and a programming language, and both are owned String values, written name: String, language: String.
Where each change lives
Consider the second version of the apology, which says This is the apology of {} and combines two sentences into one line. In the repeated version you would rewrite all three groups. With the function, you edit the body once, and the calls for Andrei, Paul and Mary stay exactly as they were. Arguments describe who, and the body describes what. Keeping those two apart is the main reason functions take parameters at all.
The body can also use a parameter more than once in a single println!. A format string with two placeholders, "{} made a mistake and so {} suffers the consequences", takes the argument name twice, once for each {}.
The exercise
The task is to write introduce_yourself, which prints two lines, and then call it for John and for Mary.
- Define the function above
mainFunctionwith twoStringparameters,nameandlanguage. Function names are snake_case in Rust, so it isintroduce_yourself. - Print the name with
println!("My name is {}", name). The{}placeholder is replaced by the next argument. - Print the language on the next line with
println!("I code in {}", language). - Introduce John by calling the function with
"John".to_string()and"Python".to_string(). A literal is a&str, and the parameters areString, so each argument needs the conversion. - Introduce Mary with a second call using
"Mary"and"JavaScript". Only the arguments change.
// Function goes here:
fn introduce_yourself(name: String, language: String) {
println!("My name is {}", name);
println!("I code in {}", language);
}
fn mainFunction() {
// Introduce John:
introduce_yourself("John".to_string(), "Python".to_string());
// Introduce Mary:
introduce_yourself("Mary".to_string(), "JavaScript".to_string());
}
The two calls print:
My name is John
I code in Python
My name is Mary
I code in JavaScript
Gotchas
- Argument order. Swapping the two arguments in a call compiles here, because both parameters are
String, but it prints "My name is Python". The compiler can catch a swap only when the types differ. - Forgetting
.to_string(). Passing a bare"John"fails with a type mismatch: expectedString, found&str. - Parameters that grow. If a function ends up with many parameters of the same type, mixing up the order becomes easy. That is a sign to group related values, which later chapters cover.
- The name
mainFunction. It is not snake_case. The compiler only warns about it, and the program still builds.
Key takeaways
- Repeated code that differs only in some values belongs in a function, with those values as parameters.
- Write the shared logic once, so each future change happens in one place.
- Calls supply the arguments: different arguments, different output, same function.
