This is my code:
fn main() {
fn fizz_buzz<'a>(i: i32) -> &'a str {
if i % 15 == 0 {
"FizzBuzz"
} else if i % 5 == 0 {
"Buzz"
} else if i % 3 == 0 {
"Fizz"
} else {
&i.to_string()
}
}
for i in 1..101 {
println!("{}" , fizz_buzz(i));
}
}
The compiler gives me this error:
error[E0515]: cannot return reference to temporary value
--> src/main.rs:11:9
|
11 | &i.to_string()
| ^-------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
For more information about this error, try `rustc --explain E0515`.
error: could not compile `playground` due to previous error
I tried a static lifetime.
Your function will correctly give back a reference to the strings "FizzBuzz," "Buzz," and "Fizz" (whose lifetimes are static since they're compiled in) however the &i.to_string()
does not have that same property. Let's look at the lifetime in detail:
When fizz_buzz
is called, i
is copied (because i32 implements the Copy trait) and given to it. In that else block, however, we do the following:
String
String
however, the lifetime of that String
is only as long as the fizz_buzz function call! Since we need to use its reference outside of that scope, Rust calls foul.
There are a couple ways to make this type safe. You could return owned values rather than references:
fn fizz_buzz(i: i32) -> String {
if i % 15 == 0 { String::from("FizzBuzz") }
else if i % 5 == 0 { String::from("Buzz") }
else if i % 3 == 0 { String::from("Fizz") }
else { i.to_string() }
}
Though this will end up creating a lot of identical objects on the heap (consider how many "Fizz"es there are, for instance)
The other option that I'd prefer is to have fizz_buzz return an Option<&str>, and have the calling scope handle the case when fizz_buzz gives None.
fn fizz_buzz(i: i32) -> Option<&'static str> {
if i % 15 == 0 { Some("FizzBuzz") }
else if i % 5 == 0 { Some("Buzz") }
else if i % 3 == 0 { Some("Fizz") }
else { None }
}
for i in 1..101 {
match fizz_buzz(i) {
Some(v) => println!("{}", v),
None => println!("{}", i),
}
}
As @RobinZigmond points out in the comments, you could also return an enum and implement Display for it.
use std::fmt::{self, Display};
enum FizzBuzz {
FizzBuzz,
Fizz,
Buzz,
Other(i32)
}
impl Display for FizzBuzz {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FizzBuzz => write!(f, "{}", "FizzBuzz"),
Self::Fizz => write!(f, "{}", "Fizz"),
Self::Buzz => write!(f, "{}", "Buzz"),
Self::Other(i) => write!(f, "{}", i.to_string())
}
}
}
fn fizz_buzz(i: i32) -> FizzBuzz {
if i % 15 == 0 { FizzBuzz::FizzBuzz }
else if i % 5 == 0 { FizzBuzz::Buzz }
else if i % 3 == 0 { FizzBuzz::Fizz }
else { FizzBuzz::Other(i) }
}
fn main() {
for i in 1..101 {
println!("{}", fizz_buzz(i));
}
}