Complete these tasks to reinforce what you learned in this module.
Write a function first_of_two<'a>(x: &'a str, y: &'a str) -> &'a str that always returns x. Add the lifetime so the compiler knows the return value is tied to the inputs. In main, call it with two string literals and print the result.
'a appears on the two input references and the return type so the compiler knows the returned reference is valid as long as the inputs. The body can simply return x.// Task 1: first_of_two with lifetime. See tasks/TASKS.md.
fn main() {
todo!("Implement first_of_two<'a> and call with \"hello\", \"world\"");
}
The function below tries to return a reference to a String that it creates internally. This is the classic borrow-checker error, and the single most important one to understand coming from C#. In C#, the GC keeps that heap String alive and you happily return a reference to it. In Rust, the local label is dropped the instant the function returns, so the reference would dangle — and the compiler refuses to build it.
The broken code in src/bin/task_02.rs is:
``rust
fn make_label(id: u32) -> &str {
let label = format!("item-{id}");
&label
}
fn main() {
let label = make_label(7);
println!("label = {label}");
}
As written, the compiler reports:
error[E0106]: missing lifetime specifier
--> src/bin/task_02.rs
|
| fn make_label(id: u32) -> &str {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is
no value for it to be borrowed from
And if you "fix" that by just naming a lifetime (-> &'a str), the error moves on to the real problem:
error[E0515]: cannot return reference to local variable label
--> src/bin/task_02.rs
|
| &label
| ^^^^^^ returns a reference to data owned by the current function
Uncomment the function and the body of main in src/bin/task_02.rs, then fix make_label` so the program compiles and prints the expected line.
label dies when the function returns. The fix is to return the owned value: change the return type to String and return label (without the &). The caller then owns the string. (The other way to satisfy the borrow checker is to borrow from an argument instead of a local, like first_of_two in Task 1 does — but here there is no input string to borrow from, so returning ownership is the right call.)// Task 2: Make it compile - fix the dangling reference. See tasks/TASKS.md.
//
// The code below tries to return a reference to a String that is created
// INSIDE the function. In C# the GC would keep that heap String alive and
// hand back a reference; in Rust the String `label` is dropped the moment
// the function returns, so the reference would dangle. The compiler refuses.
//
// Uncomment the `make_label` function below and the body of `main`, then fix
// `make_label` so the program compiles and prints the Expected line.
//
// As written (no lifetime on the return type) the compiler reports:
//
// error[E0106]: missing lifetime specifier
// --> src/bin/task_02.rs
// |
// | fn make_label(id: u32) -> &str {
// | ^ expected named lifetime parameter
// |
// = help: this function's return type contains a borrowed value, but
// there is no value for it to be borrowed from
//
// If you "fix" it by just naming a lifetime (`-> &'a str`), you only move the
// error one step on, to the real problem:
//
// error[E0515]: cannot return reference to local variable `label`
// --> src/bin/task_02.rs
// |
// | &label
// | ^^^^^^ returns a reference to data owned by the current function
//
// HINT: You cannot hand out a reference to data the function owns and drops.
// No lifetime annotation can make a dangling reference legal. Return the
// OWNED value instead: change the return type to `String` and return `label`
// (without the `&`). The caller then owns the string and prints it.
// fn make_label(id: u32) -> &str {
// let label = format!("item-{id}");
// &label
// }
fn main() {
// let label = make_label(7);
// println!("label = {label}");
todo!("Uncomment make_label and main, then fix make_label so it compiles");
}