What the code is supposed to do
The function welcome_user takes a name and should print a welcome message for that person. The program calls it for Andy and then for John, so the expected output is:
Welcome, Andy!
Welcome, John!
Instead, both lines say Andy:
Welcome, Andy!
Welcome, Andy!
fn welcome_user(name: String) {
println!("Welcome, Andy!");
}
fn mainFunction() {
welcome_user("Andy".to_string());
welcome_user("John".to_string());
}
What goes wrong
The calls are correct. The first passes "Andy", the second passes "John", and both are converted to the owned String that the parameter expects. The bug is inside the function. The body prints the fixed text Welcome, Andy! and never looks at name.
A parameter does nothing by itself. It is a variable that holds the argument, and it affects the output only if the body uses it. Here the value "John" arrives in the function and is silently dropped at the end of the call.
The compiler hints at this. It still builds the program, but warns that name is an unused variable and suggests naming it _name if that is intentional. A warning does not stop the build, which is why this bug reaches run time and shows up as wrong output instead of an error.
Fixing it step by step
- Find what changes between calls. Only the name should differ, so the name must come from the parameter.
- Replace the hard-coded name with a placeholder. In the format string,
Andybecomes{}. - Pass the parameter after the format string.
println!fills each{}with the next argument, so add, name. - Leave the calls alone. They already pass the right values.
The fixed code in Rust
fn welcome_user(name: String) {
println!("Welcome, {}!", name);
}
fn mainFunction() {
welcome_user("Andy".to_string());
welcome_user("John".to_string());
}
/* Explanation:
Notice that in the original function we didn't actually use our parameter "name".
And so the function printed "Welcome, Andy!" no matter if we passed "Mike" or "Sally" etc. to it.
The fix is to use the parameter "name" with string concatenation or string formatting to dynamically construct the message:
println!("Welcome, {}!", name);
*/
Some details about println! that matter here:
- The format string is a literal. It must be written directly in the macro call. You cannot build the message by gluing a variable onto a string with
+insideprintln!. - Placeholders are filled in order. The first
{}takes the first argument after the string, the second{}takes the second, and so on. The number of placeholders must equal the number of arguments, or the program does not compile. - Newer Rust also accepts
{name}. A variable name inside the braces is read from the surrounding scope, soprintln!("Welcome, {name}!")prints the same text. The lesson uses the{}form.
Why this bug is easy to miss
The program compiles, runs and prints a perfectly valid sentence, so nothing crashes. The only sign is that the output differs from what you meant. When a function gives the same result for different arguments, check whether its parameters are used at all. A good habit is to trace one call by hand: with name equal to "John", which line of the body mentions John? If none does, the parameter is being ignored.
Common mistakes
- Changing the calls instead of the function. Editing the arguments does nothing, because the body ignores them.
- Typing the placeholder but no argument.
println!("Welcome, {}!");fails to compile with an error about a missing argument. - Dropping the punctuation. The output must match exactly: the comma after
Welcome, the space, and the!at the end. - Silencing the warning with
_name. That removes the warning but not the bug. The fix is to use the value, not to hide that it is unused.
Checking your fix
Run the code in the editor above. After the fix, welcome_user("Andy".to_string()) prints Welcome, Andy! and welcome_user("John".to_string()) prints Welcome, John!. Any other name passed in would appear in its own message in the same way.
